)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

IOS 16 Orientation Issues

With IOS 16 all my apps that support multiple orientations are now broken when rotating the device. Apple has broken my apps so many times with stuff like this. It’s so frustrating! Anyway…

Here is how I fixed it for my apps. In my main view controller I listen for orientation changes:

  1. [[NSNotificationCenter defaultCenter] addObserver:self
  2.                                       selector:@selector(orientationChange:)
  3.                                       name:UIDeviceOrientationDidChangeNotification
  4.                                       object:nil];

That has been that way for years… works fine and calls orientationChange.

  1. - (void)orientationChange:(NSNotification *)note {
  2.   UIDevice * device = note.object;
  3.   orient = device.orientation;
  4.  
  5.   // found this by searching around github https://github.com/liLeiBest/LZDependencyToolkit/blob/bca976207ef9674632d448e62ac425c22cecbc4f/LZDependencyToolkit/Classes/Category/UIViewController+LZForceRotation.m#L107
  6.   // glad that someone figured it out :D
  7.   if (@available(iOS 16, *)) {
  8.  
  9.     [self setNeedsUpdateOfSupportedInterfaceOrientations];
  10.     [self.navigationController setNeedsUpdateOfSupportedInterfaceOrientations];
  11.     NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
  12.     if (array.count) {
  13.  
  14.       UIWindowScene *scene = (UIWindowScene *)array[0];
  15.       UIWindowSceneGeometryPreferencesIOS *geometryperences = [[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:(1 << orient)];
  16.       [scene requestGeometryUpdateWithPreferences:geometryperences errorHandler:^(NSError * _Nonnull error) {
  17.         NSLog(@"Orientation Error:%@", error);
  18.       }];
  19.     }
  20.   }
  21.   [self refreshUI];
  22. }

That big blob checking if IOS 16 is available is now necessary. Within my refreshUI method I use the width and height of the screen to position things. It seems that [[UIScreen mainScreen] bounds].size.height now takes a few hundred milliseconds to be updated with the current orientation. Maybe there is some new animation that needs to be disabled, but I have not been able to find it. So, I just delay 200ms and all seems ok:

  1. - (void) refreshUI {
  2.  
  3.   [self delay:0.2 callback:^(void){
  4.  
  5.     UIWindow* window=[[UIApplication sharedApplication] keyWindow];
  6.  
  7.     float t = window.safeAreaInsets.top;
  8.  
  9.     float b = window.safeAreaInsets.bottom;
  10.  
  11.     float l = window.safeAreaInsets.left;
  12.  
  13.     float r = window.safeAreaInsets.right;
  14.  
  15.     H = [[UIScreen mainScreen] bounds].size.height;
  16.     W = [[UIScreen mainScreen] bounds].size.width;
  17.  
  18.     // do some layout change stuff using `W` and `H`
  19.  
  20.   }];
  21. }

My delay method just looks like this:

  1. - (void)delay:(double)time callback:(void(^)(void))callback {
  2.   double delayInSeconds = time;
  3.   dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
  4.   dispatch_after(popTime, dispatch_get_main_queue(), callback);
  5. }

Beware of IOS dev – its riddled with this stuff…

// IOS // iPad // iPhone // ui

Remove a DOM HTML Element

  1. const button = document.createElement('button');
  2. button.innerText = 'Hello There';
  3. document.body.appendChild(button);
  4.  
  5. // click anywhere
  6. document.body.addEventListener('click', () => {
  7.   if (button.parentNode != null) {
  8.     button.parentNode.removeChild(button);
  9.   }
  10. });

In vanilla js you’ll find yourself checking if an HTML DOM element can be removed, by seeing if it has a parent (as seen above). Forgetting to do so is the source of many errors.

With the death of IE11 you can use remove()

  1. const button = document.createElement('button');
  2. button.innerText = 'Hello There 2';
  3. document.body.appendChild(button);
  4.  
  5. // click anywhere
  6. document.body.addEventListener('click', () => {
  7.    button.remove();
  8. });

No error occurs when calling remove() on something that is already removed… just like the old jQuery days 😉

// dom // javascript // ui

Normalize Value Between 0 and 1

  1. const d = document.body.appendChild(
  2.   document.createElement`div`)
  3. d.innerHTML = `
  4. <input 
  5.   id="input"
  6.   type="range" 
  7.   min="-2" max="5" 
  8.   value="0">
  9. `
  10.  
  11. let val = 0;
  12.  
  13. console.log('drag slider...')
  14.  
  15. const range = (input.max - input.min);
  16.  
  17. input.oninput = () => {
  18.   val = (input.value - input.min) / range
  19.   console.log(input.value + ' - normalized = ' + val)
  20. }
// html // javascript // math // ui

Little Grid

  1. const NUM = 9
  2. const col = 3
  3. const size = 30
  4. const pad = 10
  5. const box = (x, y) => Object.assign(
  6.   document.body.appendChild(
  7.     document.createElement('div')
  8.   ).style, {
  9.     position: 'absolute',
  10.     width: size + 'px', 
  11.     height: size + 'px',
  12.     top: y + 'px', 
  13.     left: x + 'px',
  14.     background: 'red'
  15. })
  16.  
  17. const off = (size + pad)
  18. for (let i = 0; i < NUM; i++) {
  19.   const x = (i % col) * off
  20.   const y = parseInt(i / col, 10) * off
  21.   box(x, y)
  22. }

Mutation Observer

  1. // Select the node that will be observed for mutations
  2. const targetNode = document.getElementById('some-id');
  3.  
  4. // Options for the observer (which mutations to observe)
  5. const config = { attributes: true, childList: true, subtree: true };
  6.  
  7. // Callback function to execute when mutations are observed
  8. const callback = function(mutationsList, observer) {
  9.     // Use traditional 'for loops' for IE 11 (goodbye IE11!!!!)
  10.     for(const mutation of mutationsList) {
  11.         if (mutation.type === 'childList') {
  12.             console.log('A child node has been added or removed.');
  13.         }
  14.         else if (mutation.type === 'attributes') {
  15.             console.log('The ' + mutation.attributeName + ' attribute was modified.');
  16.         }
  17.     }
  18. };
  19.  
  20. // Create an observer instance linked to the callback function
  21. const observer = new MutationObserver(callback);
  22.  
  23. // Start observing the target node for configured mutations
  24. observer.observe(targetNode, config);
  25.  
  26. // Later, you can stop observing
  27. observer.disconnect();

This is pure gold if you haven’t used it… (from MDN)

// dom // events // graphics // html // javascript // ui
snippet.zone ~ 2021-22 /// {s/z}