I am building full accessibility into my iOS Game called Swordy Quest: https://apps.apple.com/us/app/swordy-quest-an-rpg-adventure/id1446641513
As you can see from the screenshots on the above link, there is a Map I have created with 50x50 individual UIViews with a UIButton on each all located on a UIScrollView. With VoiceOver turned off the whole app (including the Map section) works fine - though the map can be a little slow to load at times. When I turn on VoiceOver the whole app responds fine except for the Map Section, which gets very laggy - almost unplayable on my iPhone 7 (like to have an old phone to test worst user experiences).
I have tried removing image detail if VoiceOver is turned on, but that makes no difference at all. This is making me think the lag is due to the 50 x 50 UIViews all of which have an accessibilityLabel added. Does VoiceOver start to lag badly if there are too many accessible labels on a single UIViewController?
Does anyone know a clever way to get around this? I wondered if maybe was a clever way you could turn off AccessibilityLabels except for when a UIView/UIButton is in the visible section of the UIScrollView?
Method 1: Solve OBS laggy recording by optimizing your computer If your CPU and GPU are overloaded, we can simply shut down some unnecessary processes.
If the color depth on two monitors is set to be different, the lag issue can occur. To fix the problem, you can try to set two monitors to the same color depth. To do so: 1) Right-click on the desktop and select Display settings to open the Display settings window. 2) Scroll down a little bit then click the Advanced display settings link.
Plug your phone directly to the TV to get rid of the lag. With wireless connection, especially if you don’t an HDMI adapter, there is a great chance for delays. Screen mirroring is a powerful feature you can use to display what’s on your smartphone to a larger screen.
You can check out the following methods to make OBS record smoother: Method 1: Solve OBS laggy recording by optimizing your computer. Method 2: Solve OBS laggy recording by adjusting OBS settings.
You should not instantiate and render 2500 views at once.
Modern day devices may handle it reasonably well but it still impacts performance and memory usage and should be avoided.
Supporting VoiceOver just surfaces this kind of bad practice.
The right tool to use here is a UICollectionView. Only visible views will be loaded to memory, which limits the performance impact significantly. You can easily implement custom layouts of any kind including a x/y scrollable tile-map like you need it.
Please see the following minimal implementation to get you started. You can copy the code to the AppDelegate.swift file of a freshly created Xcode project to play around with and adapt it to your needs.
// 1. create new Xcode project
// 2. delete all swift files except AppDelegate
// 3. delete storyboard
// 4. delete references to storyboard and scene from info.plist
// 5. copy the following code to AppDelegate
import UIKit
// boilerplate
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let layout = MapLayout(columnCount: 50, itemSize: 50)!
self.window?.rootViewController = ViewController(collectionViewLayout: layout)
self.window?.makeKeyAndVisible()
return true
}
}
class MapLayout: UICollectionViewLayout {
let columnCount: Int
let itemSize: CGFloat
var layoutAttributesCache = Dictionary<IndexPath, UICollectionViewLayoutAttributes>()
init?(columnCount: Int, itemSize: CGFloat) {
guard columnCount > 0 else { return nil }
self.columnCount = columnCount
self.itemSize = itemSize
super.init()
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override var collectionViewContentSize: CGSize {
let itemCount = self.collectionView?.numberOfItems(inSection: 0) ?? 0
let width = CGFloat(min(itemCount, self.columnCount)) * itemSize
let height = ceil(CGFloat(itemCount / self.columnCount)) * itemSize
return CGSize(width: width, height: height)
}
// the interesting part: here the layout is calculated
override func prepare() {
let itemCount = self.collectionView?.numberOfItems(inSection: 0) ?? 0
for index in 0..<itemCount {
let xIndex = index % self.columnCount
let yIndex = Int( Double(index / self.columnCount) )
let xPos = CGFloat(xIndex) * self.itemSize
let yPos = CGFloat(yIndex) * self.itemSize
let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: IndexPath(item: index, section: 0))
cellAttributes.frame = CGRect(x: xPos, y: yPos, width: self.itemSize, height: self.itemSize)
self.layoutAttributesCache[cellAttributes.indexPath] = cellAttributes
}
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
layoutAttributesCache.values.filter { rect.intersects($0.frame) }
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
self.layoutAttributesCache[indexPath]!
}
override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { false }
}
// boilerplate
class ViewController: UICollectionViewController {
var columnCount: Int { (self.collectionViewLayout as! MapLayout).columnCount }
var rowCount: Int { columnCount }
override func viewDidLoad() {
super.viewDidLoad()
self.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
}
override func numberOfSections(in collectionView: UICollectionView) -> Int { 1 }
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { rowCount * columnCount }
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
let fancyColor = UIColor(hue: CGFloat((indexPath.row % columnCount))/CGFloat(columnCount), saturation: 1, brightness: 1 - floor( Double(indexPath.row) / Double( columnCount) ) / Double(rowCount), alpha: 1).cgColor
cell.layer.borderColor = fancyColor
cell.layer.borderWidth = 2
return cell
}
}
The result should look something like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With