I have subclassed a UITextField and trying to add a subview by calling [self.superView addSubview:newView] but this is not working and self.superview.class returns null.
How can one add a subview to the superView
The view is showing on the screen and done using Storyboard
#interface NewTextField : UITextField <UITextFieldDelegate>
#end
and then
#implementation MBTextField
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self addWhiteLine]; // here im adding a view
}
return self;
}
- (void)addWhiteLine{
UIView *div = [UIView new];
div.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y + self.frame.size.height+borderWidth,
self.frame.size.width,
borderWidth);
div.backgroundColor = [borderColor colorWithAlphaComponent:0.3f];
[self.superview addSubview:div];
}
Solution: accepted answer and comments by #mackworth. Frame of the view had to be changed to following.
div.frame = CGRectMake(0,
self.frame.size.height - borderWidth,
self.frame.size.width,
borderWidth);
If self.superview is nil, then at the time addWhiteLine is being called, self hasn't been added to the view hierarchy yet. This actually makes sense given that you're calling it from the initWithCoder. Whoever's loading this view has to alloc/init before adding it to a superview. As a fix, why add to self.superview, shouldn't you add to self instead?
Edit: As discussed in comments, another problem is that the line is being drawn outside the frame, so it should be self.frame.origin.y + self.frame.size.height-borderWidth instead.
Related
I'm trying to place a UISearchController within my apps UI. The layout is:
Yellow: a ViewController
Red: another ViewController
Black: a container within the YellowViewController
I want to put the UISearchView of the UISearchController within the black container.
My code is the following:
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsViewController];
UISearchBar* searchBar = self.searchController.searchBar;
searchBar.frame =_searchBarContainer.bounds;
[_searchBarContainer addSubview:searchBar];
[_searchBarContainer layoutIfNeeded];
It places the UISearchBar in the correct place:
But when I select the search field it expands the bar over the container's bounds:
How can I solve that and avoid the size/appearance change when selected?
Note: Tried some options playing with the translatesAutoresizingMaskIntoConstraints and the clipToBounds options with no success. I'm not an expert of iOS UI so I would appreciate an accurate answer. Thanks
According to my research, at each time you select SearchBar, a UISearchController is presented. This UISearchController always try to make searchBar's width equals to UIViewController which is presenting UISearchController.
My solution is when UISearchController makes SearchBar has wrong frame, set SearchBar'frame again. You can try this code below.
#interface ViewController () <UISearchControllerDelegate>
#property (nonatomic, strong) UISearchController* searchController;
#property (weak, nonatomic) IBOutlet UIView *searchBarContainer;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.searchController = [[UISearchController alloc] initWithSearchResultsController:searchResultsViewController];
UISearchBar* searchBar = self.searchController.searchBar;
self.searchController.delegate = self;
searchBar.frame =_searchBarContainer.bounds;
[_searchBarContainer addSubview:searchBar];
[_searchBarContainer layoutIfNeeded];
}
- (void)willPresentSearchController:(UISearchController *)searchController {
[searchController.searchBar addObserver:self forKeyPath:#"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}
- (void)willDismissSearchController:(UISearchController *)searchController{
[searchController.searchBar removeObserver:self forKeyPath:#"frame"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (object == self.searchController.searchBar) {
if (!CGSizeEqualToSize(self.searchController.searchBar.frame.size, _searchBarContainer.frame.size)) {
self.searchController.searchBar.superview.clipsToBounds = NO;
self.searchController.searchBar.frame = CGRectMake(0, 0, _searchBarContainer.frame.size.width, _searchBarContainer.frame.size.height);
}
}
}
#end
At least, it works :)
Or
You can create another UIViewController which contains SearchBar after that add it to _searchBarContainer if your case don't have any problem with this.
Use UISearchBar and UITableView instead of UISearchController. It's easier to handle.
I have found useful infos.
There are several methods that are invoked when you tap the UISearchBar.
When some of this method are invoked, the frame of the UISearchBar change his value.
One of these methods try to fill the width equal to UIViewController.
Try to set your frame value inside one of these methods:
searchBarTextDidBeginEditing
searchBarShouldBeginEditing
In this way you override the default value.
Bye
What I mean by floating view is custom view that is a subview (or appears to be a subview) of scrollview which scrolls along until it anchors on certain point. Similar behavior would be UITableView's section header. Attached image below
My content view (the view underneath the floating view) is not in tableview layout. Meaning if I use tableview only for the floating view, I have to put my content view inside 1 giant cell or break it to several cells with different layouts. The content view will have a lot of dynamic elements which is why I don't want to put it inside UITableViewCell unless I have to. Can I make floating view programmatically / using autolayout on scrollview?
Using the tableview section header is probably the best solution, you can always easily customise the number of cells or cells themselves to achieve a particular layout.
However if you definitely don’t want to deal with a tableview, this component seems really cool, it's actually is meant to be added to a tableview, but I tested it with the twitter example and you can actually add it to a scrollview, so you don’t need a table view and it will work, give props the guy who made it. GSKStretchyHeaderView
Hope this helps, comment if you have any questions, good luck.
Use KVO to update the floating view's frame.
Here is the sample code written in Objective-C:
// ScrollView.m
// ScrollView is a subclass of UIScrollView
#interface ScrollView ()
#property (nonatomic, strong) UIView *floatingView;
#property (nonatomic) CGRect originalBorderFrame;
#property (nonatomic) CGFloat anchorHeight;
#end
#implementation ScrollView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.floatingView = [UIView new];
self.floatingView.backgroundColor = [UIColor colorWithRed:0.8211 green:0.5 blue:0.5 alpha:1.0];
self.floatingView.frame = CGRectMake(0, 150, frame.size.width, 20);
self.originalBorderFrame = self.floatingView.frame;
[self addSubview:self.floatingView];
self.anchorHeight = 44;
[self addObserver:self forKeyPath:#"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)dealloc {
[self removeObserver:self forKeyPath:#"contentOffset"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:#"contentOffset"]) {
if (self.contentOffset.y > self.originalBorderFrame.origin.y-self.anchorHeight) {
self.floatingView.frame = CGRectOffset(self.originalBorderFrame, 0, self.contentOffset.y - (self.originalBorderFrame.origin.y-self.anchorHeight));
}
}
}
#end
Here is the capture:
I have a UIView
#property (nonatomic, strong) IBOutlet UIView *previewView;
previewView will ultimately display the preview of what is going on in my camera on my iPhone using the following preview layer.
#property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
I have constraints set up on my previewView in storyboards and therefore am running into the following issue.
self.previewLayer.frame = self.previewView.bounds; // This doesn't work
self.previewLayer.videoGravity = AVLayerVideoGravityResize;
[self.previewView.layer addSublayer:self.previewLayer];
previewLayer gets added to the previewView subLayer, but it does not show in the right parameters (width and height). I think this is due to using constraints on autolayout.
How would I fix this?
Update: I also tried the following:
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspect;
CALayer *rootLayer = [previewView layer];
rootLayer.frame = self.previewView.bounds;
[rootLayer setMasksToBounds:YES];
[self.previewLayer setFrame:[rootLayer bounds]];
[rootLayer addSublayer:self.previewLayer];
And it appears to show like this: http://cl.ly/image/3T3o0O1E3U17
Getting close, but still very much off.
Okay, I solved it.
The problem is that when you use autolayout, the frames/bounds of your UIViewController's subviews can change after you have set up your previewLayer, without the previewLayer itself being updated accordingly! This is because the layout of CALayers is solely controlled by one's own code (see also this related answer: https://stackoverflow.com/a/27735617/623685).
To solve this, you have to override the viewDidLayoutSubviews method of your UIViewController to update the previewLayer's position and bounds (and possibly others) every time the subviews' layout changes.
In the following, I will describe how I solved it in detail:
My case was actually a bit more complicated, because I am using the OpenCV iOS framework 2.4.9. Here, the preview layer is created and managed by an OpenCV class called CvVideoCamera. Therefore I had to create a subclass of CvVideoCamera, to which I added the following method:
- (void)updatePreviewLayer
{
self.customPreviewLayer.bounds = CGRectMake(0, 0, self.parentView.frame.size.width, self.parentView.frame.size.height);
[self layoutPreviewLayer];
}
Explanation: parentView is a UIView you pass to CvVideoCamera in order to specify where the preview layer should be positioned and how big it should be. customPreviewLayer is the preview layer managed by CvVideoCamera. And layoutPreviewLayer is a method that basically updates the layer's position: https://github.com/Itseez/opencv/blob/2.4/modules/highgui/src/cap_ios_video_camera.mm#L211
Then I simply called this method from my UIViewController like so:
- (void)viewDidLayoutSubviews
{
NSLog(#"viewDidLayoutSubviews");
[super viewDidLayoutSubviews];
[self.myCameraView updatePreviewLayer];
}
The main idea is the same - when using Autolayout, the frame of the preview layer needs to be set after all view constraints have been applied. The viewDidLayoutSubviews method is used for this - same as in the original answer but without complications of using any frameworks other than the Apple's AVFoundation.
The following code is based on an Apple's sample (AVCam-iOS: Using AVFoundation to Capture Images and Movies). But the code was modified to use updateViewConstraints method to programmatically set all constraints instead of the Storyboard approach used in the sample. Relevant fragments are shown below:
#interface QRScannerViewController ()
#property (nonatomic) UIView *previewView;
#end
#implementation QRScannerViewController
{
AVCaptureSession *_captureSession;
AVCaptureVideoPreviewLayer *_captureVideoPreviewLayer;
dispatch_queue_t _sessionQueue;
}
#pragma mark - Lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.previewView];
_captureSession = [AVCaptureSession new];
_captureVideoPreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:_captureSession];
_captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
_sessionQueue = dispatch_queue_create("av_session_queue", DISPATCH_QUEUE_SERIAL);
[self.view setNeedsUpdateConstraints];
dispatch_async(_sessionQueue, ^{
[self configureCaptureSession];
});
[self.previewView.layer addSublayer:_captureVideoPreviewLayer];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (_isCaptureDeviceConfigured) {
[_captureSession startRunning];
}
}
- (void)viewDidLayoutSubviews
{
_captureVideoPreviewLayer.frame = self.previewView.bounds;
}
#pragma mark - getters / initializers
- (UIView *)previewView
{
if (_previewView) {
return _previewView;
}
_previewView = [UIView new];
_previewView.backgroundColor = [UIColor blackColor];
_previewView.translatesAutoresizingMaskIntoConstraints = NO;
return _previewView;
}
- (void)configureCaptureSession
{
[_captureSession beginConfiguration];
// Code omitted, only relevant to the preview layer is included. See the Apple's sample for full code.
...
_captureVideoPreviewLayer.connection.videoOrientation = initialVideoOrientation;
...
}
#pragma mark - Autolayout
- (void)updateViewConstraints
{
[self.previewView.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant:0.0].active = YES;
[self.previewView.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor constant:-40.0].active = YES;
[self.previewView.widthAnchor constraintEqualToConstant:300].active = YES;
[self.previewView.heightAnchor constraintEqualToAnchor:self.previewView.widthAnchor constant:0.0].active = YES;
[super updateViewConstraints];
}
I have 2 Custom View classes(CustomView_A, CustomView_B) derived from UIView. I have UIViewController that should be able to switch between views at run-time..
What so far, I have done is.. in the Storyboard, I am using CustomView_A class as the View class.
#interface MyViewController: UIViewController
#property (nonatomic, weak) CustomView_A *customView_A;
Now I have the second CustomView_B class and I want to change view of MyViewController's view to CustomView_B at run-time.
How can I do that? Thanks in advance..
okay, here is the code as you want -
in your MyViewController.h put -
#property (nonatomic, retain) CustomView_A *customView_A;
#property (nonatomic, retain) CustomView_B *customView_B;
-(void)switchView; // to switch the views.
in your MyViewController.m put -
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.customView_A = [[CustomView_A alloc]initWithFrame:self.view.frame];
self.customView_A.backgroundColor = [UIColor redColor];
UIButton *trigger = [UIButton buttonWithType:UIButtonTypeRoundedRect]; // Just take this button so that your switchView methods will get called on click of this method.
[trigger setFrame:CGRectMake(10, 10, 50, 30)];
[trigger setTitle:#"Click" forState:UIControlStateNormal];
[trigger addTarget:self action:#selector(switchView) forControlEvents:UIControlEventTouchUpInside];
[self.customView_A addSubview:trigger];
[self.view addSubview:self.customView_A];
self.customView_B = [[CustomView_B alloc]initWithFrame:self.view.frame];
self.customView_B.backgroundColor = [UIColor yellowColor];
self.customView_B.hidden = YES;
[self.view addSubview:self.customView_B];
}
- (void)switchView
{
[UIView animateWithDuration:10 delay:10 options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.customView_A.hidden = YES;
self.customView_B.hidden = NO;
} completion:nil];
}
Do opposite when you again want to switch the views.
Don't. What you're describing is an essential misunderstanding of UIViewController. Once a UIViewController instance has a view, that is its view forever.
If you want two different views then either:
Use two view controllers (for example, you can present view controller B and its view on top of view controller A and its view, using a modal segue), or
Make at least one of those views not be owned by a view controller: just place that view in front of the other view and later remove it again, at will.
Try this:
- (IBAction)changeView {
if (self.customView_A.hidden == YES) {
self.customView_A.hidden = NO;
self.customView_B.hidden = YES;
//You should use a UIView animation here to do this.
}
else {
self.customView_A.hidden = YES;
self.customView_B.hidden = NO;
//Same here
)
}
In your viewDidLoad add the view to CGRectZero
- (void)viewDidLoad {
self.customView_A = [[CustomView_A alloc]initWithFrame:CGRectZero];
[self.view addSubview:self.customView_A];
//do the same with the other custom view
}
Sorry if the code is a little faulty, I didn't use Xcode to type this up.
I'm looking for popover in iPhone and i want to make it like iOS 5 Reader feature:
After little research i found WEPopover and FPPopover but i'm looking if there anything like this API built-in iphone SDK.
You could make a UIView with some custom artwork and display it with an animation on top of your view as a "popover" with some buttons like so:
UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(25, 25, 100, 50)]; //<- change to where you want it to show.
//Set the customView properties
customView.alpha = 0.0;
customView.layer.cornerRadius = 5;
customView.layer.borderWidth = 1.5f;
customView.layer.masksToBounds = YES;
//Add the customView to the current view
[self.view addSubview:customView];
//Display the customView with animation
[UIView animateWithDuration:0.4 animations:^{
[customView setAlpha:1.0];
} completion:^(BOOL finished) {}];
Don't forget to #import <QuartzCore/QuartzCore.h>, if you want to use the customView.layer.
Since iOS8 we are now able to create popovers, that will be the same on iPhone, as on iPad, which would be especially awesome for those who make universal apps, thus no need to make separate views or code.
You can get the class as well as demo project here: https://github.com/soberman/ARSPopover
All you need to do is subclass UIViewController, conform to the UIPopoverPresentationControllerDelegate protocol and set desired modalPresentationStyle along with the delegate value:
// This is your CustomPopoverController.m
#interface CustomPopoverController () <UIPopoverPresentationControllerDelegate>
#end
#implementation CustomPopoverController.m
- (instancetype)init {
if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationPopover;
self.popoverPresentationController.delegate = self;
}
return self;
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone; //You have to specify this particular value in order to make it work on iPhone.
}
Afterwards, instantiate your newly created subclass in the method from which you want to show it and assign two more values to sourceView and sourceRect. It looks like this:
CustomPopoverController *popoverController = [[CustomPopoverController alloc] init];
popoverController.popoverPresentationController.sourceView = sourceView; //The view containing the anchor rectangle for the popover.
popoverController.popoverPresentationController.sourceRect = CGRectMake(384, 40, 0, 0); //The rectangle in the specified view in which to anchor the popover.
[self presentViewController:popoverController animated:YES completion:nil];
And there you have it, nice, neat blurred popover.