How to add a safe area programmatically - ios

When you open the view, it will look like the image below,
i Phone x open view
i Phone 8 open view
For iphone x, I would like to add a safe area programmatically in the current view.
The source to try is as follows.
UIView *view = self.view;
if (#available(iOS 11.0, *)) {
UILayoutGuide * guide = view.safeAreaLayoutGuide;
[view.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
[view.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;
}
I suppose to apply this source, but I do not know what to do.
please answer about my question.!

Here is sample code for Safe Area Layout. Try this in Objective-C and see:
UIView * myView = // initialize view using IBOutlet or programtically
myView.backgroundColor = [UIColor red];
myView.translatesAutoresizingMaskIntoConstraints = NO;
UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
[self.myView.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
[self.myView.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
[self.myView.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
[self.myView.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;
// Refresh myView and/or main view
[self.view layoutIfNeeded];
//[self.myView layoutIfNeeded];
Ref from: Use Safe Area Layout programmatically
Result:

Related

Date pickers selection's overlay not adjusting when screen rotates

I've got a view with two date pickers set to cover the entire view's height and half a width each, so that they are filling the entire view.
I've then added an overlay to each picker to make the selection more visible, like this:
-(void)drawOverlays {
if (_overlay1 != nil) {
[_overlay1 removeFromSuperview];
}
if (_overlay2 != nil) {
[_overlay2 removeFromSuperview];
}
_overlay1 = [[UIView alloc] initWithFrame:CGRectMake(_startPicker.bounds.origin.x, (_startPicker.frame.size.height/2)-19, _startPicker.bounds.size.width, 38)];
_overlay1.backgroundColor = [UIColor redColor];
_overlay1.alpha = 0.5f;
[_startPicker addSubview:_overlay1];
_overlay2 = [[UIView alloc] initWithFrame:CGRectMake(_endPicker.bounds.origin.x, (_endPicker.frame.size.height/2)-19, _endPicker.bounds.size.width, 38)];
_overlay2.backgroundColor = [UIColor redColor];
_overlay2.alpha = 0.5f;
[_endPicker addSubview:_overlay2];
}
I'm calling this method from the -viewDidLayoutSubviews method and from the -viewWillTransitionToSize:withTransitionCoordinator method, and the first time the view appears everything is fine.
Then I rotate my iPad and the overlays are shown inverted, meaning that when in landscape the overlays are the size I want for the portrait and vice versa.
What's wrong with my code?
You will be much better off using constraints and letting auto-layout handle the resizing:
-(void)drawOverlays {
if (_overlay1 != nil) {
[_overlay1 removeFromSuperview];
}
if (_overlay2 != nil) {
[_overlay2 removeFromSuperview];
}
//_overlay1 = [[UIView alloc] initWithFrame:CGRectMake(_startPicker.bounds.origin.x, (_startPicker.frame.size.height/2)-19, _startPicker.bounds.size.width, 38)];
// instantiate overlay1
_overlay1 = [UIView new];
_overlay1.backgroundColor = [UIColor redColor];
_overlay1.alpha = 0.5f;
// add as subview of startPicker
[_startPicker addSubview:_overlay1];
//_overlay2 = [[UIView alloc] initWithFrame:CGRectMake(_endPicker.bounds.origin.x, (_endPicker.frame.size.height/2)-19, _endPicker.bounds.size.width, 38)];
// instantiate overlay2
_overlay2 = [UIView new];
_overlay2.backgroundColor = [UIColor redColor];
_overlay2.alpha = 0.5f;
// add as subview of endPicker
[_endPicker addSubview:_overlay2];
// we want to use auto-layout / constraints
_overlay1.translatesAutoresizingMaskIntoConstraints = NO;
_overlay2.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:#[
// constrain overlay1 to startPicker
// centerY
// leading / trailing = 0
// height = 38
[_overlay1.centerYAnchor constraintEqualToAnchor:_startPicker.centerYAnchor],
[_overlay1.leadingAnchor constraintEqualToAnchor:_startPicker.leadingAnchor constant:0.0],
[_overlay1.trailingAnchor constraintEqualToAnchor:_startPicker.trailingAnchor constant:0.0],
[_overlay1.heightAnchor constraintEqualToConstant:38.0],
// constrain overlay2 to startPicker
// centerY
// leading / trailing = 0
// height = 38
[_overlay2.centerYAnchor constraintEqualToAnchor:_endPicker.centerYAnchor],
[_overlay2.leadingAnchor constraintEqualToAnchor:_endPicker.leadingAnchor constant:0.0],
[_overlay2.trailingAnchor constraintEqualToAnchor:_endPicker.trailingAnchor constant:0.0],
[_overlay2.heightAnchor constraintEqualToConstant:38.0],
]
];
}
And, this only needs to be called from viewDidLoad (or wherever else you may find it appropriate). There is no need for it to be -- and in fact, it should not be -- called from viewDidLayoutSubviews or viewWillTransitionToSize.
As a side note -- if you are using remove and re-add to show and hide them, you'll also get a little better optimization if you add them once, and then set the .hidden property to YES or NO.

Xcode 11: Safe Area code implementation using Objective-C not working

I am new to iOS application development and I am currently working on an existing iOS application written using Objective-C. I came across a requirement where I need to implement Safe Area programmatically. So I have implemented the following piece of code in "viewDidLoad" method.
if(#available(iOS 11, *)){
UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
[self.view.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
[self.view.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
[self.view.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
[self.view.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;
}else{
UILayoutGuide * margins = self.view.layoutMarginsGuide;
[self.view.leadingAnchor constraintEqualToAnchor:margins.leadingAnchor].active = YES;
[self.view.trailingAnchor constraintEqualToAnchor:margins.trailingAnchor].active = YES;
[self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
[self.view.bottomAnchor constraintEqualToAnchor:self.bottomLayoutGuide.topAnchor].active = YES;
}
But, when I try to run the application connecting to iPhone XR iOS Simulator device, the above code is not working. The view is not getting started after the safe area in landscape mode instead it is getting started from the safe area itself due to which the text is getting cut off
Am I missing anything? Can you please suggest on what needs to be done so that the safe area can be implemented?
What UI element are you trying to layout in your view? You mention text is getting cut off. The code you provided is setting the anchor from the view to itself and not to the UI element. Update your code to set the anchors of the UI element to the view.
UITextField *textField; // Your text UI element
UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
[textField.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
[textField.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
[textField.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
[textField.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;
I'd recommend you to use
constraintEqualToSystemSpacingAfterAnchor: multiplier:
and
constraintEqualToSystemSpacingBelowAnchor: multiplier:
instead of
constraintEqualToAnchor:
Also I'd recommend you to try setting translatesAutoresizingMaskIntoConstraints to NO manually:
textField.translatesAutoresizingMaskIntoConstraints = NO;
The full code:
UITextField *textField = [[UITextField alloc] init];
textField.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:textField];
if (#available(iOS 11, *)) {
UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
[textField.leadingAnchor constraintEqualToSystemSpacingAfterAnchor:guide.leadingAnchor
multiplier:1.0].active = YES;
[textField.trailingAnchor constraintEqualToSystemSpacingAfterAnchor:guide.trailingAnchor
multiplier:-1.0].active = YES;
[textField.topAnchor constraintEqualToSystemSpacingBelowAnchor:guide.topAnchor
multiplier:1.0].active = YES;
[textField.bottomAnchor constraintEqualToSystemSpacingBelowAnchor:guide.bottomAnchor
multiplier:1.0].active = YES;
} else {
// ...
}

Removing the blank spaces on left and right side of the UISearchBar

I am creating a UI component that should display a UILabel and a UISearchBar below.
However, I am not able to align them, the UISearchBar always has extra space on both left and right side (highlighted RED).
I was trying to set the dimensions of searchBarTextField by layout anchors directly, but it didn't work.
I would prefer to do it using layout anchors when possible.
SearchBar.m:
-(id) init {
self.titleLabel = [UILabel new];
self.searchBar = self.searchController.searchBar;
UIView *searchBarWrapper = [UIView new];
[self.view addSubview:self.titleLabel];
[self.view addSubview:searchBarWrapper];
[searchBarWrapper addSubview:self.searchBar];
[self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[searchBarWrapper setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.searchBar setTranslatesAutoresizingMaskIntoConstraints:NO];
//[self.titleLabel.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:1.0].active = YES;
[self.titleLabel.heightAnchor constraintEqualToConstant:14.0].active = YES;
[self.titleLabel.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[self.titleLabel.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
[self.titleLabel.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
//[self.titleLabel.bottomAnchor constraintEqualToAnchor:searchBarTextField.topAnchor].active = YES;
[searchBarWrapper.widthAnchor constraintEqualToAnchor:self.view.widthAnchor multiplier:1.0].active = YES;
//[self.searchBar.heightAnchor constraintEqualToConstant:36.0].active = YES;
[searchBarWrapper.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor].active = YES;
[searchBarWrapper.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;
[searchBarWrapper.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
[searchBarWrapper.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
[self.searchBar.topAnchor constraintEqualToAnchor:searchBarWrapper.topAnchor].active = YES;
[self.searchBar.bottomAnchor constraintEqualToAnchor:searchBarWrapper.bottomAnchor].active = YES;
[self.searchBar.leftAnchor constraintEqualToAnchor:searchBarWrapper.leftAnchor].active = YES;
[self.searchBar.rightAnchor constraintEqualToAnchor:searchBarWrapper.rightAnchor constant:16.0].active = YES;
return self;
}
The UISearchBarTextField is embedded in a UIView to which I don't have access to.
The constraints:
The approach by #BenOng - works fine, but breaks after tapping for the first time:
UISearchBar have this property searchFieldBackgroundPositionAdjustment:UIOffset which you can set to UIOffsetMake(-8,0) to make the left side of the text field align with the left edge of the search bar.
Create a UIView right where the full search bar should be and make the search bar it's subview. Set the right edge of the search bar beyond it's super view till the right edge aligns with the superview, it should be about 16 points extra. Make sure the superview's property clipsToBoundsis set to true, the extra 16 points of search bar background will be clipped.

How to disable the animation scrolling in a UIScrollView in Objective C?

I loaded a pdf in a scrollview, and I would like to go directly to the next page without having the animation scrolling.
CGRect scrollViewRect = CGRectInset(viewRect, -scrollViewOutset, 0.0f);
theScrollView = [[UIScrollView alloc] initWithFrame:scrollViewRect]; // All
theScrollView.autoresizesSubviews = NO;
theScrollView.contentMode = UIViewContentModeRedraw;
theScrollView.showsHorizontalScrollIndicator = NO;
theScrollView.showsVerticalScrollIndicator = NO;
theScrollView.scrollsToTop = NO;
theScrollView.delaysContentTouches = NO;
theScrollView.pagingEnabled = YES;
theScrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
theScrollView.backgroundColor = [UIColor clearColor];
theScrollView.delegate = self;
[self.view addSubview:theScrollView];
Is it possible ?
You have to use below method to jump on direct offset of your next page by below method.
[objUIScrollView setContentOffset:CGPointMake(YOUR_X, YOUR_Y) animated:NO]
animated = NO;
If you are loading PDF through WebView than you need to follow below code:
webview.scrollView.contentOffset = CGPointMake(YOUR_X, YOUR_Y);
Note : Use this above code in your Next Button page or after loading
WebView properly may be in below method.
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
}
Hope this will work as you expected.
Sure, simply use:
[theScrollView scrollRectToVisible:CGRectMake(0.0f, 0.0f, 0.0f, 0.0f) animated:NO];
or
[theScrollView setContentOffset:CGPointMake(0.0f, 0.0f) animated:NO];
and change the CGPoint or CGRect to the offset of the page of your liking.

How to prevent UIView from being resized to fit ScrollView height (autoresizing disabled)?

I'm writing a PDF reader using vfr-reader library. To display two pages in landscape i'm rendering each page into its own view, then add these two views to a container view, then add container view to a scroll view. Each view's autoresizingMask is set to UIViewAutoresizingNone, contentMode is UIViewContentModeRedraw, autoresizingSubviews is set to 'NO' for all views.
But still somehow container view is autoresized to fit scroll view's height, and I don't know where is this happening. I care about this because, when autoresizing container view, it's width becomes larger than screen width, and I cannot scroll to the next two pages with a single swipe (two swipes are needed), which sucks. What am I missing?
EDIT gonna add some come if it helps. In the ViewController I create a Scroll View with following options:
theScrollView = [[ReaderScrollView alloc] initWithFrame:viewRect];
theScrollView.scrollsToTop = NO;
theScrollView.pagingEnabled = YES;
theScrollView.delaysContentTouches = NO;
theScrollView.showsVerticalScrollIndicator = NO;
theScrollView.showsHorizontalScrollIndicator = NO;
theScrollView.contentMode = UIViewContentModeRedraw;
theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theScrollView.backgroundColor = [UIColor clearColor];
theScrollView.userInteractionEnabled = YES;
theScrollView.autoresizesSubviews = NO;
theScrollView.delegate = self;
[self.view addSubview:theScrollView];
When I'm drawing pages I'm adding an UIView to the Scroll View, which is being initiated this way:
if ((self = [super initWithFrame:frame]))
{
self.autoresizesSubviews = YES;
self.userInteractionEnabled = YES;
self.contentMode = UIViewContentModeRedraw;
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.backgroundColor = [UIColor clearColor];
theScrollView = [[ReaderScrollView alloc] initWithFrame:self.bounds]; // Not sure about this part - why is the 2nd scroll view added?
// this is the way its done in vfr reader
theScrollView.scrollsToTop = NO;
theScrollView.delaysContentTouches = NO;
theScrollView.showsVerticalScrollIndicator = NO;
theScrollView.showsHorizontalScrollIndicator = NO;
theScrollView.contentMode = UIViewContentModeRedraw;
theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
theScrollView.backgroundColor = [UIColor clearColor];
theScrollView.userInteractionEnabled = YES;
theScrollView.autoresizesSubviews = NO;
theScrollView.bouncesZoom = YES;
theScrollView.delegate = self;
theContentView = [[ReaderContentPage alloc] initWithURL:fileURL page:page password:phrase landscape:(BOOL)isLandscape position:FALSE];
CGRect viewRect = CGRectZero;
viewRect.size.width = theContentView.bounds.size.width;
viewRect.size.height = theContentView.bounds.size.height;
if( isLandscape){
NSLog(#"Landscape detected in content view");
if (theContentView == NULL)
{
theContentView = [[ReaderContentPage alloc] initWithURL:fileURL page:(page+1) password:phrase landscape:(BOOL)isLandscape position:FALSE];
theContentView2 = NULL;
viewRect.size.width = theContentView.bounds.size.width;
viewRect.size.height = theContentView.bounds.size.height;
} else {
if (page == 1)
theContentView2 = NULL;
else
theContentView2 = [[ReaderContentPage alloc] initWithURL:fileURL page:(page+1) password:phrase landscape:(BOOL)isLandscape position:TRUE];
if (theContentView2 != NULL)
viewRect.size.width = theContentView.bounds.size.width*2;
}
}
if (theContentView != nil) // Must have a valid and initialized content view
{
theContainerView = [[UIView alloc] initWithFrame:viewRect];
theContainerView.autoresizesSubviews = NO;
theContainerView.userInteractionEnabled = NO;
theContainerView.contentMode = UIViewContentModeRedraw;
theContainerView.autoresizingMask = UIViewAutoresizingNone;
theContainerView.backgroundColor = [UIColor whiteColor];
theScrollView.contentSize = theContentView.bounds.size; // Content size same as view size
theScrollView.contentOffset = CGPointMake((0.0f - CONTENT_INSET), (0.0f - CONTENT_INSET));
theScrollView.contentInset = UIEdgeInsetsMake(CONTENT_INSET, CONTENT_INSET, CONTENT_INSET, CONTENT_INSET);
theThumbView = [[ReaderContentThumb alloc] initWithFrame:theContentView.bounds]; // Page thumb view
[theContainerView addSubview:theThumbView]; // Add the thumb view to the container view
[theContainerView addSubview:theContentView]; // Add the content view to the container view
if(( isLandscape) && (theContentView2 != NULL)){
[theContainerView addSubview:theContentView2]; // Add the content view to the container view
}
[theScrollView addSubview:theContainerView]; // Add the container view to the scroll view
[self updateMinimumMaximumZoom]; // Update the minimum and maximum zoom scales
theScrollView.zoomScale = theScrollView.minimumZoomScale; // Zoom to fit
}
[self addSubview:theScrollView]; // Add the scroll view to the parent container view
[theScrollView addObserver:self forKeyPath:#"frame" options:NSKeyValueObservingOptionNew context:NULL];
self.tag = page; // Tag the view with the page number
}
return self;
And ReaderContentPage is created this way:
if ((self = [super initWithFrame:frame]))
{
self.autoresizesSubviews = NO;
self.userInteractionEnabled = NO;
self.clearsContextBeforeDrawing = NO;
self.contentMode = UIViewContentModeRedraw;
self.autoresizingMask = UIViewAutoresizingNone;
self.backgroundColor = [UIColor clearColor];
view = self; // Return self
}
In the function updateMinimumMaximumZoom in the class ReaderContentView:
The calculation of the zoomscale to fit the screen is done with single view, but in landscape it should be calculated form theContainerView.
Try replacing this code
CGFloat zoomScale = ZoomScaleThatFits(targetRect.size, theContentView.bounds.size);
with
CGFloat zoomScale = ZoomScaleThatFits(targetRect.size, theContainerView.bounds.size);
There is a contentMode property in UIScrollView, change it to UIViewContentModeCenter or something else to prevent resizing.

Resources