React Native native UI component width and height - react-native

I'm creating a native UI component on iOS and I want its size to expand according to the content size.
It seems like I must set a fixed width and height in order for the view to be rendered. Any idea how to solve it?
// JS
import React from 'react';
import { View, requireNativeComponent } from 'react-native';
class StyledText extends React.Component {
render() {
return (
<View style={this.props.style}>
// without the height and width the compnent won't show up
<StyledLabelReactBridge styledText={'some text'} style={{height: 100, width: 100, backgroundColor: 'red'}}/>
</View>
);
}
}
StyledText.propTypes = {
styledText: React.PropTypes.string,
style: View.propTypes.style
};
const StyledLabelReactBridge = requireNativeComponent('StyledLabelReactBridge', StyledText);
module.exports = StyledText;
// objective-C
#implementation StyledLabelReactBridgeManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [[NewStyledLabel alloc] init];
}
RCT_CUSTOM_VIEW_PROPERTY(styledText, NSString, NewStyledLabel)
{
if (![json isKindOfClass:[NSString class]])
return;
[view setStyledText:[NewStyledText textFromXHTML:json]];
}
#end

You need to override reactSetFrame in xcode to receive content size change.
#import "React/UIView+React.h"
#implementation YourView {
- (void)reactSetFrame:(CGRect)frame {
[super reactSetFrame: frame];
/* everytime content size changes, you will get its frame here. */
}
}

first you should create a subclass of RCTShadowView like
#import <React/RCTShadowView.h>
#interface RNGuessLikeContainerShadowView : RCTShadowView
#end
#implementation RNGuessLikeContainerShadowView
- (void)setLocalData:(NSObject *)localData {
if ([localData isKindOfClass:[NSNumber class]]) {
[self setIntrinsicContentSize:CGSizeMake(UIScreen.mainScreen.bounds.size.width, ((NSNumber *)localData).floatValue)];
}
}
#end
then create subclass of RCTViewManager and return shadowview and view of you custom class instance
#import <React/RCTUIManager.h>
#import <React/RCTUIManagerUtils.h>
#interface RNGuessLikeModule: RCTViewManager <RNGuessLikeContainerViewHeightUpdater>
#end
#implementation RNGuessLikeModule
RCT_EXPORT_MODULE(RNGuessLikeModule)
RCT_EXPORT_VIEW_PROPERTY(objects, NSString);
- (UIView *)view {
RNGuessLikeContainerView *_view = [RNGuessLikeContainerView new];
_view.delegate = self;
return _view;
}
- (RCTShadowView *)shadowView {
return [RNGuessLikeContainerShadowView new];
}
- (void)didUpdateWithHeight:(CGFloat)height view:(RNGuessLikeContainerView *)view {
RCTExecuteOnUIManagerQueue(^{
RCTShadowView *shadowView = [self.bridge.uiManager shadowViewForReactTag:view.reactTag];
[shadowView setLocalData:#(height)];
[self.bridge.uiManager setNeedsLayout];
});
}
#end
and in mine code i set custom native ui view delegate to RNGuessLikeModule which is subclass of RCTViewManager,
and you can caculate size in you custom view when data from rn module passed
#objc
public protocol RNGuessLikeContainerViewHeightUpdater {
func didUpdate(height: CGFloat, view: RNGuessLikeContainerView)
}
public final class RNGuessLikeContainerView: UIView, GuessLikeItemsComponentContainer {
#objc
public var objects: String? {
didSet {
if let _objects = objects,
let _data = _objects.data(using: .utf8, allowLossyConversion: true) {
reload(objects: _data)
}
}
}
#objc
public weak var delegate: RNGuessLikeContainerViewHeightUpdater?
public var controller: UIViewController {
return reactViewController()
}
public var guessLikeSceneType: GuessLikeSceneType = .邀好友赚现金红包
public var guessLikeTitle: String?
public var guessLikeItems: [GuessLikeItemsSectionSubItem] = []
public var routerInfo: String?
#objc
public private(set) var guessLikeHeight: CGFloat = 0
lazy var backend = GuessLikeItemsComponentContainerBackend(parent: self)
public lazy var guessLikeContainer: UICollectionView = {
let _container = createGuessLikeContainer()
_container.dataSource = backend
_container.delegate = backend
addSubview(_container)
return _container
}()
override public func layoutSubviews() {
super.layoutSubviews()
guessLikeContainer.frame = bounds
}
public func reload(objects: Data) {
precondition(pthread_main_np() != 0, "RNGuessLikeContainerView reload method should be called on main thread")
do {
let _items = try JSONDecoder().decode([ItemListModel].self, from: objects)
guessLikeItems = GuessLikeItemsSectionItem(list: _items).items
guessLikeContainer.reloadData()
updateHeight()
} catch {
debugPrint(error)
}
}
public func append(objects: Data) {
precondition(pthread_main_np() != 0, "RNGuessLikeContainerView append method should be called on main thread")
if let _list = try? JSONDecoder().decode([ItemListModel].self, from: objects) {
let _items = GuessLikeItemsSectionItem(list: _list).items
guessLikeItems.append(contentsOf: _items)
guessLikeContainer.reloadData()
updateHeight()
}
}
func updateHeight() {
if guessLikeItems.isEmpty {
guessLikeHeight = 0
} else {
var leftHeight: CGFloat = 0
var rightHeight: CGFloat = 0
for (index, item) in guessLikeItems.enumerated() {
if index % 2 == 0 {
leftHeight += item.height + 10.0
} else {
rightHeight += item.height + 10.0
}
}
let sectionHeaderHeight: CGFloat = 50.0
guessLikeHeight = max(leftHeight, rightHeight) + sectionHeaderHeight
}
if let _delegate = delegate {
_delegate.didUpdate(height: guessLikeHeight, view: self)
}
}
public override var intrinsicContentSize: CGSize {
return CGSize(width: UIScreen.main.bounds.width, height: guessLikeHeight)
}
}
then find shadowview binded to your custom ui view and update intrinsicContentSize
finaly call [self.bridge.uiManager setNeedsLayout]
may help you

#implementation SNBFundCardViewManager
RCT_EXPORT_MODULE(FundCard)
- (UIView *)view
{
return [[SNBFundHomeFundCardCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#""];
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_CUSTOM_VIEW_PROPERTY(data, NSDictionary, SNBFundHomeFundCardCell)
{
view.rnData = json;
// 自己撑起高度
CGFloat height = [view.vm getCellHeight];
[self.bridge.uiManager setIntrinsicContentSize:CGSizeMake(UIViewNoIntrinsicMetric, height) forView:view];
}

Well, couldn't find 'auto' like behavior, how ever, setting the component to:
{{ width: '100%', height: '100%}}
Makes it expand (and shrink) according to the parent, which is good enough for my use case. It's a shame that setting 'flex: 1' doesn't have the same effect.

Related

How can I animate a text change in swift or objective-c?

I tried a test-project. It worked but the method I used, put programmatically created labels over old existing labels. When the length of a string array is lower than the old one, it shows unnecessary old labels due to addSubview method.
How can I handle this problem?
import UIKit
extension UIView {
func pushTransition(duration:CFTimeInterval) {
let animation:CATransition = CATransition()
animation.timingFunction = CAMediaTimingFunction(name:
kCAMediaTimingFunctionEaseInEaseOut)
animation.type = kCATransitionPush
animation.subtype = kCATransitionFromBottom
animation.duration = duration
self.layer.addAnimation(animation, forKey: kCATransitionPush)
}
}
class ViewController: UIViewController {
var current:Float = 125.24
var discount:Float = 1.212342748
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func doAction(sender: AnyObject) {
let currentstr = String(format: "₺%.2f", self.current)
let length = currentstr.characters.count
var strarray = Array(currentstr.characters)
strarray[length-3] = ","
print(strarray)
self.current = self.current-self.discount
let newcurrent = String(format: "₺%.2f", self.current)
let newcurrentlength = newcurrent.characters.count
var newcurrentarray = Array(newcurrent.characters)
newcurrentarray[newcurrentlength-3] = ","
print(newcurrentarray)
var labels = [UILabel]()
print(labels)
if (length == newcurrentlength) {
for i in 1 ..< length+1 {
labels.append(UILabel(frame: CGRectMake((15*CGFloat(i)), 100, 20, 20)))
if (strarray[i-1] == newcurrentarray[i-1]){
print("SAME")
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
} else {
print("CHANGED")
labels[i-1].pushTransition(0.4)
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
}
}
} else {
for i in 1..<newcurrentlength+1 {
labels.append(UILabel(frame: CGRectMake((15*CGFloat(i)), 100, 20, 20)))
if (strarray[i-1] == newcurrentarray[i-1]){
print("SAME")
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
} else {
print("CHANGED")
labels[i-1].pushTransition(0.4)
labels[i-1].text = String(newcurrentarray[i-1])
labels[i-1].textAlignment = NSTextAlignment.Center
labels[i-1].backgroundColor = UIColor.redColor()
self.view.addSubview(labels[i-1])
}
}
}
}
}
EDIT
I put label array out of action section and it's fixed.
var labels = [UILabel]()
However, the transition between 100,00 and 99,99 there is a problem with the last child of label array. It still shows last digit like 99,990

Emulate / send Modifier Key (Cntrl, Alt, fn, Shift) in OSx

I am sending keyboard key press and key release events, which works for all keyboard keys.
But modifier keys works only when the key associated with modifier key is sent from application, and not from real hardware. That is if I send
Shift and 'a' from application, it prints 'A' (capital A, which is what is expected).
But if I send 'shift' key down event from application, and enter 'a' from physical keyboard, it prints 'a' (the shift key doesn't seem to be working across different devices). The same applies for other modifier keys such as cmd, alt, and fn keys!.
Is there a way to send modifier keys to system, so that I can emulate modifier keys from my application?.
Specifically, I would like to activate modifier key from application, and enter the combination key from physical keyboard.
Here is the code I use to send key press and release events.
- (void)setADBKey:(uint32)key value:(int)value
{
if (!eventSource)
{
eventSource = CGEventSourceCreate(kCGEventSourceStatePrivate);
}
CGEventRef event = CGEventCreateKeyboardEvent(eventSource, key, value!=0);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
}
I have also tried creating and sending a system event, through the following code
struct
{
CGKeyCode keyCode;
int flag;
int cgEventFlag;
} modifiers[] = {
{ 56, NX_DEVICELSHIFTKEYMASK, kCGEventFlagMaskShift },
{ 60, NX_DEVICERSHIFTKEYMASK, kCGEventFlagMaskShift },
{ 59, NX_DEVICELCTLKEYMASK, kCGEventFlagMaskControl },
{ 58, NX_DEVICELALTKEYMASK, kCGEventFlagMaskAlternate },
{ 61, NX_DEVICERALTKEYMASK, kCGEventFlagMaskAlternate },
{ 55, NX_DEVICELCMDKEYMASK, kCGEventFlagMaskCommand },
{ 54, NX_DEVICERCMDKEYMASK, kCGEventFlagMaskCommand }
};
- (void)setAdbKey:(uint32)adbkey value:(int)value repeat:(BOOL)repeat
{
//int adbkey = def_usb_2_adb_keymap[hidkey];
int modifier = 0;
for(int i=0; i< ARR_SIZE(modifiers); i++)
{
if (adbkey == modifiers[i].keyCode)
{
modifier = modifiers[i].cgEventFlag;
break;
}
}
if (value)
{
flags |= modifier;
}
else
{
flags &= ~modifier;
}
CGEventRef event = CGEventCreateKeyboardEvent(eventSource, adbkey, value!=0);
if (repeat)
{
CGEventSetIntegerValueField(event, kCGKeyboardEventAutorepeat, (int64_t)1);
}
// don't apply modifier flags to a modifier
if (!modifier)
{
CGEventSetFlags(event, flags);
}
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
}
Both methods work when modifier key and combination key are sent from application, but not when modifier key alone is set from application.
All you have to do is create an event tap and set the mask.
Sample:
#interface AppDelegate ()
#property (assign) CFMachPortRef myEventTap;
#property (assign) CFRunLoopSourceRef myRunLoopSource;
#end
#implementation AppDelegate
CGEventRef MyEventTapCallBack(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
CGEventSetFlags(event, kCGEventFlagMaskShift);
return event;
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.myEventTap = CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventFlagsChanged),
MyEventTapCallBack,
(__bridge void *)self);
if (self.myEventTap) {
self.myRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, self.myEventTap, 0);
if (self.myRunLoopSource)
CFRunLoopAddSource(CFRunLoopGetMain(), self.myRunLoopSource, kCFRunLoopCommonModes);
}
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
if (self.myRunLoopSource) {
CFRunLoopSourceInvalidate(self.myRunLoopSource);
CFRelease(self.myRunLoopSource);
}
if (self.myEventTap)
CFRelease(self.myEventTap);
}
#end
This is my appDelegate and how I got it working.
import Cocoa
import CoreGraphics
import Foundation
var shift = false;
var cntrl = false;
var option = false;
var command = false;
func myCGEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, refcon: UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? {
var mask: CGEventFlags = []
if(shift) {
mask.insert(CGEventFlags.maskShift)
}
if(cntrl) {
mask.insert(CGEventFlags.maskControl)
}
if(option) {
mask.insert(CGEventFlags.maskAlternate)
}
if(command) {
mask.insert(CGEventFlags.maskCommand)
}
if(mask.rawValue != 0) {
event.flags = mask;
}
return Unmanaged.passRetained(event)
}
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Insert code here to initialize your application
let mask: CGEventMask = 1 << CGEventType.keyDown.rawValue
let eventTap:CFMachPort = CGEvent.tapCreate(tap: .cghidEventTap, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: mask, callback: myCGEventCallback, userInfo: nil)!
let runLoopSource:CFRunLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, CFRunLoopMode.commonModes);
CGEvent.tapEnable(tap: eventTap, enable: true);
CFRunLoopRun();
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
public func SetShift() {
shift = !shift;
}
public func SetCtrl() {
cntrl = !cntrl;
}
public func SetCommand() {
command = !command;
}
public func SetOption() {
option = !option;
}
}

Converting MKPolygon Objective-C code to Swift

I have the following Objective-C method:
- (void)addPolygonToMap {
NSInteger numberOfPoints = [self.coordinates count];
if (numberOfPoints > 4) {
CLLocationCoordinate2D points[numberOfPoints];
for (NSInteger i = 0; i < numberOfPoints; i++) {
points[i] = [self.coordinates[i] MKCoordinateValue];
}
self.polygon = [MKPolygon polygonWithCoordinates:points count:numberOfPoints];
[self.mapView addOverlay: self.polygon];
}
self.isDrawingPolygon = NO;
[self.drawPolygonButton setTitle:#"draw" forState:UIControlStateNormal];
self.canvasView.image = nil;
[self.canvasView removeFromSuperview];
}
My attempt at converting it to Swift:
func addPolygonToMap() {
var numberOfPoints: NSInteger = self.coordinates.count
if (numberOfPoints > 4) {
var points: [CLLocationCoordinate2D] = []
var coordsPointer = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(numberOfPoints)
for i in 0..<numberOfPoints {
points.append(coordsPointer[i])
}
self.polygon = MKPolygon(coordinates: &points, count: numberOfPoints)
self.mapView.addOverlay(self.polygon)
coordsPointer.dealloc(numberOfPoints)
}
self.isDrawingPolygon = false
self.drawPolygonButton.setTitle("Draw", forState: .Normal)
self.canvasView.image = nil
self.canvasView.removeFromSuperview()
}
Finally, when the delegate method is called it's not actually adding the overlay to the mapView. I can't see anything.
I'm assuming it's my self.addPolytonToMap() method, but I'm not 100% sure. The whole scenario works fine in my Objective-C project.
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if (overlay is MKPolygon) {
var overlayPathView = MKPolygonRenderer(overlay: overlay)
overlayPathView.fillColor = UIColor.cyanColor().colorWithAlphaComponent(0.2)
overlayPathView.strokeColor = UIColor.cyanColor().colorWithAlphaComponent(0.2)
overlayPathView.lineWidth = 5
return overlayPathView
} else if (overlay is MKPolyline) {
var overlayPathView = MKPolylineRenderer(overlay: overlay)
overlayPathView.strokeColor = UIColor.blueColor().colorWithAlphaComponent(0.7)
overlayPathView.lineWidth = 5
return overlayPathView
}
return nil
}
UPDATE:
I just noticed that in my Objective-C version, the points[i].latitude are coming through okay, however when I do the following I get a strange output:
println(coordsPointer[i].latitude)
Output:
1.63041663127611e-321
1.64523860065135e-321
1.65511991356818e-321
1.68970450877706e-321
1.7045264781523e-321
1.72922976044436e-321
This would explain why I don't see the overlay, however my experience with UnsafeMutablePointer<> is limited.
Fixed by modifying the addPolygonToMap() method:
func addPolygonToMap() {
var numberOfPoints: NSInteger = self.coordinates.count
if (numberOfPoints > 4) {
var points: [CLLocationCoordinate2D] = []
for i in 0..<numberOfPoints {
points.insert(self.coordinates[i].MKCoordinateValue, atIndex: i)
}
self.polygon = MKPolygon(coordinates: &points, count: numberOfPoints)
self.mapView.addOverlay(self.polygon)
}
self.isDrawingPolygon = false
self.drawPolygonButton.setTitle("Draw", forState: .Normal)
self.canvasView.image = nil
self.canvasView.removeFromSuperview()
}
Thanks to #Volker for the help.

Separate Landscape and Portrait Views in iOS8 using Objective C

I have separate landscape and portrait views in iOS7 using the code below:
-(void)didRotateFromInterfaceOrientation: (UIInterfaceOrientation)fromInterfaceOrientation {
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) {
CGRect currentBounds=self.view.bounds;
if (iPadInt==0) {
if (self.interfaceOrientation==UIInterfaceOrientationLandscapeRight) {
self.view=self.landscapeQuestionView;
self.view.transform=CGAffineTransformMakeRotation((kDeg2Rad)*(90));
} else if (self.interfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
self.view=self.landscapeQuestionView;
self.view.transform=CGAffineTransformMakeRotation((kDeg2Rad)*(-90));
} else if (self.interfaceOrientation==UIInterfaceOrientationPortrait) {
self.view=self.portraitQuestionView;
self.view.transform=CGAffineTransformMakeRotation(0);
} else if (self.interfaceOrientation==UIInterfaceOrientationPortraitUpsideDown) {
self.view=self.portraitQuestionView;
self.view.transform=CGAffineTransformMakeRotation((kDeg2Rad)*(180));
} self.view.bounds=currentBounds;
} else if (iPadInt==1) {
if (self.interfaceOrientation==UIInterfaceOrientationLandscapeRight) {
self.view=self.iPadLandscapeQuestionView;
self.view.transform=CGAffineTransformMakeRotation((kDeg2Rad)*(90));
rotationInt=90;
} else if (self.interfaceOrientation==UIInterfaceOrientationLandscapeLeft) {
self.view=self.iPadLandscapeQuestionView;
self.view.transform=CGAffineTransformMakeRotation((kDeg2Rad)*(-90));
rotationInt=90;
} else if (self.interfaceOrientation==UIInterfaceOrientationPortrait) {
self.view=self.iPadPortraitQuestionView;
self.view.transform=CGAffineTransformMakeRotation(0);
rotationInt=0;
} else if (self.interfaceOrientation==UIInterfaceOrientationPortraitUpsideDown) {
self.view=self.iPadPortraitQuestionView;
self.view.transform=CGAffineTransformMakeRotation((kDeg2Rad)*(180));
rotationInt=0;
} self.view.bounds=currentBounds;
}
}
I know I should use the viewWillTransitionToSize method in iOS8, but I can't figure out how to get these different views in that method. Thanks in advance for any help.
There is a topic here: Rotation methods deprecated, equivalent of 'didRotateFromInterfaceOrientation'?
I think in your situation you can use viewWillTransitionToSize: like this:
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
//get orientation
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
//check orientation
if (orientation==UIInterfaceOrientationPortrait)
{
NSLog(#"portrait");
}
else if(orientation==UIInterfaceOrientationPortraitUpsideDown)
{
NSLog(#"upside-down");
}
else if(orientation==UIInterfaceOrientationLandscapeRight)
{
NSLog(#"landscape right");
}
else if(orientation==UIInterfaceOrientationLandscapeLeft)
{
NSLog(#"landscape left");
}
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
}];
}

iOS8 video dimension, CMVideoDimensions returns 0,0

in iOS8 the dimension returned is 0,0
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
This was working on iOS7, so how to know the supported video dimension, as i need to know the video aspect ratio
You need to wait for the AVCaptureInputPortFormatDescriptionDidChangeNotification
- (void)avCaptureInputPortFormatDescriptionDidChangeNotification:(NSNotification *)notification {
AVCaptureInput *input = [self.recorder.captureSession.inputs objectAtIndex:0];
AVCaptureInputPort *port = [input.ports objectAtIndex:0];
CMFormatDescriptionRef formatDescription = port.formatDescription;
if (formatDescription) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(formatDescription);
if ((dimensions.width == 0) || (dimensions.height == 0)) {
return;
}
CGFloat aspect = (CGFloat)dimensions.width / (CGFloat)dimensions.height;
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {
// since iOS8 the aspect ratio is inverted
// remove this check if iOS7 will not be supported
aspect = 1.f / aspect;
}
}
}
Provided you're tracking the device being used, you can access the current format from activeFormat: https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVCaptureDevice_Class/index.html#//apple_ref/occ/instp/AVCaptureDevice/activeFormat
I recently ran into this particular issue, here's the Swift 5 version for those who need it too:
import Foundation
import AVFoundation
class MySessionManager: NSObject {
static let notificationName = "AVCaptureInputPortFormatDescriptionDidChangeNotification"
let session: AVCaptureSession
var videoCaptureDimensions: CMVideoDimensions?
init(session: AVCaptureSession) {
self.session = session
let notificationName = NSNotification.Name()
NotificationCenter.default.addObserver(
self,
selector: #selector(formatDescription(didChange:)),
name: .init(Self.notificationName),
object: nil
)
}
deinit { NotificationCenter.default.removeObserver(self) }
#objc func formatDescription(didChange notification: NSNotification) {
guard
let input = session.inputs.first,
let port = input.ports.first,
let formatDesc = port.formatDescription
else { return }
var dimensions = CMVideoFormatDescriptionGetDimensions(formatDesc)
// ... perform any necessary dim adjustments ...
videoCaptureDimensions = dimensions
}
}