SwiftUI: Как создать несколько видов в различных положениях с таймером? - PullRequest
1 голос
/ 24 февраля 2020

Довольно просто подготовить следующую задачу в старой школе: каждые три секунды новый вид (подпредставление) появляется в новой позиции. Вот код:

import UIKit

class ViewController: UIViewController {

var someView = UIView()
var posX : CGFloat = 10
var posY : CGFloat = 10
var timer:Timer!
var loopCount = 1


override func viewDidLoad() {
    super.viewDidLoad()

    startTimer()
    view.backgroundColor = .purple

}

func setView() {
    someView = UIView(frame: CGRect(x: posX, y: posY, width: 10, height: 10))
    someView.backgroundColor = UIColor.orange
    view.addSubview(someView)
}

func startTimer() {
    if timer == nil {
        timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(loop), userInfo: nil, repeats: true)
    }
}

@objc func loop(){
    if (loopCount % 3 == 0) {
        posX += 15
        posY += 15
        setView()
    }
    loopCount += 1
 }


 }

SwiftUI значительно упрощает многие вещи, но, боюсь, не эту. По крайней мере, я не мог найти простой способ решить это до сих пор. У кого-нибудь есть идеи?

Вот экран с результатом (через несколько секунд):

enter image description here

1 Ответ

1 голос
/ 24 февраля 2020

Здесь возможен подход (протестировано с Xcode 11.2 / iOS 13.2). SwiftUI - это реактивная концепция, поэтому вместо добавления самого представления в модель представления добавляется новая позиция, а представление SwiftUI в ответ на это изменение модели представления обновляет sh само добавление нового вида (в данном случае Rectangle) в новой добавленной позиции. .

Демонстрация (момент начала записи не точен, но регулярные добавления добавляются):

enter image description here

Код: (см. Также некоторые встроенные комментарии)

   // needed to use as ID in ForEach
    extension CGPoint: Hashable {
        public func hash(into hasher: inout Hasher) {
            hasher.combine(self.x)
            hasher.combine(self.y)
        }
    }

   // View model holding and generating new positions
    class DemoViewModel: ObservableObject {
        @Published var positions = [CGPoint]() // all points for view

        private var loopCount = 0
        func loop() {
            if (loopCount % 3 == 0) {
                if let last = positions.last { // generate new point
                    positions.append(CGPoint(x: last.x + 15, y: last.y + 15))
                } else {
                    positions.append(CGPoint(x: 10, y: 10))
                }
            }
            loopCount += 1
        }
    }

    struct DemoAddingView: View {
        @ObservedObject var vm = DemoViewModel()

        let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
        var body: some View {
            ZStack {
                ForEach(vm.positions, id: \.self) { position in
                    Rectangle().fill(Color.orange) // just generate a rect view for all points
                        .frame(width: 10, height: 10)
                        .position(position) // location of rect in global coordinates
                }
                .onReceive(timer) { _ in
                    self.vm.loop() // add next point
                }
            }
        }

    }
...