Нормальный узел начинает неправильно перенаправляться на первичный узел сбоя, а не предполагаемые подключения.
Эта проблема сохраняется до тех пор, пока пользователь не освежает страницу.
Стоимость сами узлы никогда не перенаправляют неправильно, то есть только нормальные узлы повреждены. < /P>
Ожидаемое поведение -< /p>
Только вторичные узлы должны перенаправить на соответствующие их первичные узлы.
узлы сбоев не должны мешать логике навигации нормальных узлов. Переплата. .
подтвердил, что сбочные узлы отображались правильно, но некоторые нормальные узлы исчезали или неправильно перенаправляли. Между узлами после встречи с обалке retrypecificsparring (), чтобы увидеть, решила ли проблема в попытке повторного. Основные данные были сохранены неверные отношения.
подтвердил, что проблема исчезает после освежения, что означает, что это, вероятно, не основная проблема хранения данных, а что -то происходит динамически во время навигации.
Что я ожидал < /p>
Вторичные узлы должны перенаправляться только на их соответствующие первичные узлы.
Отказы сбоев не должны мешать нормальным узлам.
После отказа нормальные узлы должны продолжать функционировать, как и ожидалось. Спарринг должен правильно сбросить поведение узла. УЗО. Это все мои относительные кусочки кода. < /P>
func navigateToPrimaryNode(for node: DragableNode) -> DragableNode {
//
if node.type == .primary {
return node
}
//
if node.type == .secondary, let primaryNode = findPrimaryNode(for: node) {
print("
return primaryNode
}
//
if node.entity.nodeType == .fail, let primaryFailNode = findPrimaryNode(for: node) {
print("
return primaryFailNode
}
//
if node.entity.nodeType != .fail {
if let cachedPrimary = findPrimaryNode(for: node) {
print("
return cachedPrimary
}
}
print("
return node
}
< /code>
Piece 2 < /p>
func findPrimaryNode(for node: DragableNode) -\> DragableNode? {
guard node.type == .secondary else { return nil }
let primaryCandidates = textFields.filter {
$0.type == .primary && $0.text == node.text
}
if primaryCandidates.isEmpty {
print("
return nil
}
if primaryCandidates.count == 1 {
print("
return primaryCandidates.first
}
//
for primary in primaryCandidates {
if primary.entity.children?.contains(node.entity) == true {
print("
return primary
}
}
print("
return primaryCandidates.first
}
< /code>
Piece 3 < /p>
// Helper function to find the primary text field with a specific name
func findPrimaryTextField(named name: String) -\> DragableNode? {
return textFields.first { $0.text == name && $0.type == .primary }
}
< /code>
Piece 4 < /p>
@objc func handleNodeTap(\_ sender: UITapGestureRecognizer) {
guard let tappedNode = sender.view as? DragableNode else { return }
//
if tappedNode.type == .secondary, let primaryNode = findPrimaryNode(for: tappedNode) {
print("
enterViewMode(for: primaryNode)
return
}
if isInViewMode {
enterViewMode(for: tappedNode)
return
}
if isInSpecificSparring {
print("
startSpecificSparring(for: tappedNode, difficulty: currentSparringDifficulty)
//
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
let visibleNodes = self.contentView.subviews.compactMap { $0 as? DragableNode }.filter { !$0.isHidden }
self.recenterAndZoomToFit(nodes: visibleNodes)
}
return
}
// Animate the current node scaling up
UIView.animate(withDuration: 0.3, animations: {
tappedNode.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
tappedNode.alpha = 1.0
}, completion: { _ in
// Reset the scaling back to normal
UIView.animate(withDuration: 0.2) {
tappedNode.transform = CGAffineTransform.identity
}
//
self.enterViewMode(for: tappedNode)
})
}
< /code>
Piece 5 < /p>
@objc private func retrySpecificSparring() {
guard let firstNode = firstViewModeNode else {
print("
if let lastUsedNode = textFields.first(where: { !$0.isHidden }) {
firstViewModeNode = lastUsedNode
startSpecificSparring(for: lastUsedNode, difficulty: currentSparringDifficulty)
}
return
}
print("
//
DispatchQueue.main.async {
for subview in self.contentView.subviews {
if let textNode = subview as? DragableNode {
textNode.isHidden = true
textNode.alpha = 0.0
}
}
//
self.contentView.layer.sublayers?.removeAll(where: { $0 is CAShapeLayer })
//
self.startSpecificSparring(for: firstNode, difficulty: self.currentSparringDifficulty)
}
}
< /code>
Piece 6 < /p>
func startSpecificSparring(for node: DragableNode, difficulty: Int) {
guard let superview = self.contentView else { return }
var targetNode: DragableNode = navigateToPrimaryNode(for: node)
// Debugging
print("
print("
if firstViewModeNode == nil {
firstViewModeNode = targetNode
print("
}
//
DispatchQueue.main.async {
targetNode.isHidden = false
targetNode.alpha = 1.0
self.contentView.bringSubviewToFront(targetNode)
}
//
for subview in contentView.subviews {
if let primaryNode = subview as? DragableNode, primaryNode.type == .primary {
contentView.bringSubviewToFront(primaryNode)
}
}
isInSpecificSparring = true
currentSparringDifficulty = difficulty
//
if firstViewModeNode == nil {
firstViewModeNode = targetNode
}
//
let winChance = Int.random(in: 1...10)
//
let (winningNumbers, losingNumbers) = getWinningLosingNumbers(for: difficulty)
let didWin = winningNumbers.contains(winChance)
print("
//
for subview in superview.subviews {
if let textNode = subview as? DragableNode {
textNode.isHidden = true
}
}
//
targetNode.isHidden = false
targetNode.alpha = 1.0
superview.bringSubviewToFront(targetNode)
//
for sublayer in superview.layer.sublayers ?? [] {
if let line = sublayer as? CAShapeLayer {
line.removeFromSuperlayer()
}
}
var nodesToShow: [DragableNode] = []
//
for subview in superview.subviews {
if let textNode = subview as? DragableNode, textNode !== targetNode {
let isConnected = targetNode.entity.children?.contains(where: { ($0 as? TextFieldsEntity) == textNode.entity }) == true
let isParent = textNode.entity.parent == targetNode.entity
let isFailNode = textNode.entity.nodeType == .fail
let isAllowedSuccessType = textNode.entity.nodeType == .progression ||
textNode.entity.nodeType == .transition ||
textNode.entity.nodeType == .submission
print("
if didWin {
//
if isFailNode {
textNode.isHidden = true
print("
}
//
else if isConnected && isAllowedSuccessType {
print("
nodesToShow.append(textNode)
}
} else {
//
if isConnected && isFailNode {
print("
nodesToShow.append(textNode)
}
}
}
}
//
for node in nodesToShow {
node.isHidden = false
node.alpha = 1.0
}
//
fadeInConnectedNodes(nodesToShow)
//
positionNodesRelativeTo(targetNode, connectedNodes: nodesToShow)
//
recenterAndZoomToFit(nodes: [targetNode] + nodesToShow)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
let visibleNodes = [targetNode] + nodesToShow
self.recenterAndZoomToFit(nodes: visibleNodes)
}
//
addRetryAndExitButtons(below: targetNode)
}
< /code>
А вот есть менеджер узлов и узел Draggable, если вам это нужно < /p>
import Foundation
import UIKit
class NodeManager {
static let shared = NodeManager()
var primaryNodes: [String: (node: DragableNode, viewController: UIViewController)] = [:]
func clearPrimaryNodes() {
print("
primaryNodes.removeAll()
}
func addPrimaryNode(_ node: DragableNode, viewController: UIViewController) {
primaryNodes[node.text ?? ""] = (node, viewController)
}
func findPrimaryNode(named name: String) -> (node: DragableNode, viewController: UIViewController)? {
return primaryNodes[name]
}
}
import UIKit
class DragableNode: UIView, UITextFieldDelegate {
var textField: UITextField
var entity: TextFieldsEntity
var lines: [CAShapeLayer] = []
private var panGesture: UIPanGestureRecognizer!
var isInEditMode = false
var isEditable: Bool = false {
didSet {
textField.isUserInteractionEnabled = isEditable
}
}
var identifier: UUID?
var isPrimary: Bool = false
var isSecondary: Bool = false
var text: String? {
get { return textField.text }
set { textField.text = newValue}
}
var attributedPlaceholder: NSAttributedString? {
get { return textField.attributedPlaceholder }
set { textField.attributedPlaceholder = newValue }
}
var type: TextFieldType = .none {
didSet {
typeChanged(type: type)
}
}
// END OF STAGE
init(size: CGSize, entity: TextFieldsEntity) {
let frame = CGRect(x: entity.xPosition, y: entity.yPosition, width: size.width, height: size.height)
self.textField = UITextField(frame: frame)
self.entity = entity
super.init(frame: frame)
self.type = entity.fieldType
typeChanged(type: type)
setupView()
setupGestures()
NotificationCenter.default.addObserver(self, selector: #selector(handleEditModeChanged(_:)), name: .editModeChanged, object: nil)
}
init(frame: CGRect, entity: TextFieldsEntity) {
self.textField = UITextField(frame: frame)
self.entity = entity
super.init(frame: frame)
self.type = entity.fieldType
typeChanged(type: type)
setupView()
setupGestures()
NotificationCenter.default.addObserver(self, selector: #selector(handleEditModeChanged(_:)), name: .editModeChanged, object: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("Method not available")
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.selectAll(_:)) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
private func setupView() {
textField.delegate = self
addSubview(textField)
textField.translatesAutoresizingMaskIntoConstraints = false
//
backgroundColor = entity.nodeType.color.withAlphaComponent(0.3)
//
entity.nodeType.applyStyle(to: self)
layer.masksToBounds = false // Allows shadow visibility
topAnchor.constraint(equalTo: textField.topAnchor).isActive = true
bottomAnchor.constraint(equalTo: textField.bottomAnchor).isActive = true
leadingAnchor.constraint(equalTo: textField.leadingAnchor, constant: -8).isActive = true
trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 8).isActive = true
}
private func setupGestures() {
panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
self.addGestureRecognizer(panGesture)
}
func startEditing() {
textField.becomeFirstResponder()
}
@objc private func handleEditModeChanged(_ notification: Notification) {
if let userInfo = notification.userInfo, let isInEditMode = userInfo["isInEditMode"] as? Bool {
self.isInEditMode = isInEditMode
}
}
@objc func handlePan(_ sender: UIPanGestureRecognizer) {
guard let superview = self.superview else { return }
// Calculate the translation
let translation = sender.translation(in: superview)
// Update the node's center position
self.center = CGPoint(x: self.center.x + translation.x, y: self.center.y + translation.y)
sender.setTranslation(.zero, in: superview)
// Update the entity's position in the data model
entity.xPosition = self.frame.origin.x
entity.yPosition = self.frame.origin.y
// Notify the parent view controller to handle additional updates (e.g., lines)
var nextResponder: UIResponder? = self
while nextResponder != nil {
if let scrollViewController = nextResponder as? ScrollViewController {
scrollViewController.updateLinePosition(for: self)
break
}
nextResponder = nextResponder?.next
}
// Handle the gesture state for further updates or actions
if sender.state == .ended {
// Optionally perform any cleanup or additional updates when the gesture ends
print("
}
}
func findViewController() -> UIViewController? {
var nextResponder: UIResponder? = self
while nextResponder != nil {
if let viewController = nextResponder as? UIViewController {
return viewController
}
nextResponder = nextResponder?.next
}
return nil
}
private func typeChanged(type: TextFieldType) {
switch type {
case .primary:
self.layer.borderColor = UIColor.black.cgColor
case .secondary:
self.layer.borderColor = UIColor.white.cgColor
case .none:
self.layer.borderColor = UIColor.clear.cgColor
}
self.layer.borderWidth = 2
entity.type = type.rawValue
}
func textFieldDidEndEditing(_ textField: UITextField) {
entity.text = textField.text
}
}
Подробнее здесь: https://stackoverflow.com/questions/794 ... edirection
Мобильная версия