UITextview text not showing top lines after rotation in iOS7 - ios7

I have an app that contains a UITextview that displays static text. I used a UITextview to get scrolling for the text, which is much longer than can be displayed in a UILabel. For some reason, the text in a UITextview under iOS 7 does not stay scrolled to the top after a rotation. This works as expected when run under iOS 6.
This can be shown by creating a project with a UITextview that is centered on the storyboard with margins around 50. Then add constraints that pin the UITextview to the edges of the main view. Be sure the text field contains enough text to cause scrolling:
Now run the app and the text will be correctly positioned in the UITextview, with the first line show at the top. After rotation, the text will be shifted down:
After rotating back to portrait, the text is still scrolled down a few lines.
All of this works perfectly if you run it in iOS 6. I have tried have done a lot of research and have tried the following possible solutions in viewWillLayoutSubviews to make the text stay in position:
[textView sizeToFit];
[textView layoutIfNeeded];
[textView setContentOffset:CGPointMake(0.0, 0.0)];
[textView scrollRectToVisible:CGRectMake(0.0, 0.0, 1.0, 1.0) animated:NO];
Nothing seems to work. Does anyone know how to keep the text in position?

Try this:
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self.textView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
}
I tested this on iOS 7.1.

Call the UITextView's scrollRangeToVisible: message from the delegate view controller's viewDidLayoutSubviews notification method. This will adjust the range after rotation as well as at first impression.
-(void)viewDidLayoutSubviews {
[self scrollToTop];
}
-(void) scrollToTop {
[self.textView setScrollEnabled:NO];
[self.textView scrollRangeToVisible:NSMakeRange(0.0f, 0.0f)];
[self.textView setScrollEnabled:YES];
}

i had the same problem. It looks like using UITextView from the storyboard create lots of small annoying bugs (like this). I managed to solve the problem by creating the UITextView programmatically:
-(void) viewDidLoad{
self.textView=[[UITextView alloc] init];
self.textView.translatesAutoresizingMaskIntoConstraints=NO;
[self.view addSubview:self.textView];
NSDictionary* views = #{#"textView": self.textView};
NSArray* vContrains=[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[textView]-50-|" options:NSLayoutFormatAlignAllLeft metrics:nil views:views];
NSArray* hContrains=[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-50-[textView]-50-|" options:NSLayoutFormatAlignAllLeft metrics:nil views:views];
[self.view addConstraints:vContrains];
[self.view addConstraints:hContrains];
}
-(void)viewWillLayoutSubviews{
[self.textView layoutIfNeeded];
[self.textView sizeToFit];
}

have tried this on iOS 8.
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
self.textView.contentInset = UIEdgeInsetsMake(0,0,0,0);
}

Similar to some of the above:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
self.textView.scrollRectToVisible(CGRect(x:0,y:0,width:1,height:1),animated: false)
self.textView.layoutIfNeeded()
self.textView.sizeToFit()
}

Related

How to add QLPreviewController as Subview in objective C - iOS8

In previous iOS versions I used to add the QLPreviewController as a subview. It is very handy to use my own app headers and navigation bar but in iOS 8 it adds a white space just below the header. It is the space for its own navigator bar.
You can see the attached img:
I use this code:
QLPreviewController *previewController = [[QLPreviewController alloc] init];
previewController.dataSource = self;
previewController.delegate = self;
previewController.currentPreviewItemIndex = 0;
previewController.view.frame = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self addChildViewController:previewController];
[previewController didMoveToParentViewController:self];
[self.containerView addSubview:previewController.view];
How can i mantain the iOS7 funcionality? I only want to hide the qlpreviewcontroller navigationbar
Thanks
I'm solving the EXACT same problem. The only solution I found so far is the following:
// qlController.view.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));//self.view.bounds;
// qlController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self addChildViewController:qlController];
[self.view addSubview:qlController.view];
NSDictionary *bindings = #{#"qlPreviewController": qlController.view};
qlController.view.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[qlPreviewController]|" options:0 metrics:nil views:bindings]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[qlPreviewController]|" options:0 metrics:nil views:bindings]];
[qlController didMoveToParentViewController:self];
Commented lines are legacy code, that worked perfectly on iOs7. The main idea is stop using spring and struts and start using auto layout. Results looks good enough, but still there are some problems with rotations.
Works good:
Iphone 4s/5/6/6+ iOs7 portrait + landscape, iOs8 portrait
IPad all models iOs7,8 portrait + landscape
Works bad:
Iphone 4s/5/6/6+ iOs8 landscape : has some spacing between navBar and content. I think it is problem with Apple's QLPreviewController rather than my code.

Set navigation bar separator color IOS

Hi I am developing IOS application in which I tried to set navigation bar separator color in different ways but it's not working for me. I tried in following ways:
[self.navigationController.navigationBar.layer setBorderWidth:2.0];// Just to make sure its working
[self.navigationController.navigationBar.layer setBorderColor:[[UIColor redColor] CGColor]];
using above I can change border color of full navigation bar but I want to change just separator color only.
I tried another method:
UIView *navBorder = [[UIView alloc] initWithFrame:CGRectMake(0,navBarCont.navigationBar.frame.size.height,navBarCont.navigationBar.frame.size.width, 1)];
[navBorder setBackgroundColor:[UIColor colorWithWhite:255.0f/255.f alpha:0.1f]];
[navBorder setOpaque:YES];
[navBarCont.navigationBar addSubview:navBorder];
This method work as I want but only thing is that when I rotate my device it will not change according to that. That mean if initially my device is in portrait mode it will show seperator in proper width but once I rotate my device to landscape it will not adjust width according to that.
So I tried to implement device orientation change listener also
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(deviceOrientationDidChangeNotification:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
- (void)deviceOrientationDidChangeNotification:(NSNotification*)note
{
[self setNavbar];
}
Above method causes one problem it keeps adding sublayer of separator view. So I have two options now one is put some auto layout constraints on added subview; or second one is every time remove old subview and then add new. But I dont know how to do that. Or is there any easy way to do this? Need some help. Thank you.
This provides a solution for not just any color, but also the height of the seperator:
- (void)viewDidLoad
{
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [self imageWithColor:[UIColor redColor] size:(CGSizeMake(self.view.frame.size.width, 1.0f))];
self.navigationController.navigationBar.translucent = YES;
}
- (UIImage*) imageWithColor:(UIColor*)color size:(CGSize)size
{
UIGraphicsBeginImageContext(size);
UIBezierPath* rPath = [UIBezierPath bezierPathWithRect:CGRectMake(0., 0., size.width, size.height)];
[color setFill];
[rPath fill];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

NSLayoutConstraint behaving differently when drawing to NSBitmapImageRep

I have this control called ITNavigationView on Github.
It's smoothly animating from one NSView to another by caching and adding them to a NSImageView.
When caching the view, a subview centred in the x axis will be pulled exactly 1 pixel to the right.
If I instead add a leading constraint, this doesn't happen.
How can I prevent this from happening?
To cache the view, I'm using this code:
- (NSImage *)imageOfView:(NSView *)view {
[view layoutSubtreeIfNeeded];
[view setNeedsUpdateConstraints:YES];
[view updateConstraintsForSubtreeIfNeeded];
NSBitmapImageRep* rep = [view bitmapImageRepForCachingDisplayInRect:view.bounds];
[view cacheDisplayInRect:view.bounds toBitmapImageRep:rep];
return [[NSImage alloc] initWithCGImage:[rep CGImage] size:view.bounds.size];
}
EDIT
Also worth noting is that this only happens when the superview has an odd width.

applicationMusicPlayer volume notification

I am using an applicationMusicPlayer and when i try to change the volume appear the visual notification, as shown in the picture.
Here the code I am using:
[MPMusicPlayerController applicationMusicPlayer] setVolume:newVolune];
Anyone knows how to hide this notification?
I don't know where the docs says so, but if you add a MPVolumeView view to your app the system volume overlay goes away. Even if it is not visible:
- (void) viewDidLoad
{
[super viewDidLoad];
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame: CGRectZero];
[self.view addSubview: volumeView];
[volumeView release];
...
}
You can use the hardware volume buttons, the setVolume method or directly interact with the control (if visible) that the overlay doesn't show up.
For iOS6 I had to set an image with alpha 0 and non-zero size to the MPVolumeView's image fields in order to get the default volume change notification to disappear.
// hide the hardware volume slider
UIImage *thumb = [[UIImage alloc] initWithCIImage:[UIImage imageNamed:#"volumeHider"].CIImage scale:0.0 orientation:UIImageOrientationUp];
MPVolumeView *hwVolume = [[MPVolumeView alloc] initWithFrame:self.frame];
[hwVolume setUserInteractionEnabled:NO];
hwVolume.showsRouteButton = NO;
[hwVolume setVolumeThumbImage:thumb forState:UIControlStateNormal];
[hwVolume setMinimumVolumeSliderImage:thumb forState:UIControlStateNormal];
[hwVolume setMaximumVolumeSliderImage:thumb forState:UIControlStateNormal];
[self addSubview:hwVolume];
This made the MPVolumeView be "visible" on the screen, but invisible to the user.
I encountered the same issue recently. Instead of adding the MPVolumeView to current view controller's view, I add it to the application's window once at the start of the app:
CGRect rect = CGRectMake(-500, -500, 0, 0);
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:rect];
[self.window addSubview:volumeView];
This works in both iOS 7 and 8.
Swift 3
You can hide the System MPVolumeView using
override func viewDidLoad() {
super.viewDidLoad()
let volumeView = MPVolumeView(frame: CGRect.zero)
self.view.addSubview(volumeView)
}
I had success with this in iOS 6. Although it wouldn't perform well. It caused quite a bit of lag when sliding the thumbImage. I did have to take out the last 2 lines of code in order for this to work.
[volumeView release];
...
For me, on iOS 7, none of above solutions worked. Here is how I did it:
_volume = [[MPVolumeView alloc] initWithFrame: CGRectMake(-100,-100,16,16)];
_volume.showsRouteButton = NO;
_volume.userInteractionEnabled = NO;
[self.view addSubview:_volume];
[_volume release];
That is, simply set MPVolumeView's frame to an off-screen location such as (-100,-100).

IOS Managing the Keyboard - Move UIScrollView Content up

I am using the example in the IOS Developer Library for managing the keyboard.
Text, Web and Edition Programming Guide
I have created a view using the IB. Its a simple UI that has a UIScrollView, UITextView, UIButton, and a UITextField. I placed the UIScrollView on my view and then added all the other controls as children of this scrollview. The scrollview is exposed to the viewcontroller via a IBOutlet "scrollView".
The follow code executes with the user sets focus to the textField but I never see a scrollbar appear and the scrollbar's contents are not moved. Should I be able to see the scrollbar by default? Can someone tell me what I'm missing?
-(void) keyboardWasShown:(NSNotification *)aNotification{
NSDictionary * info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
scrollView.contentInset = contentInsets;
scrollView.scrollIndicatorInsets = contentInsets;
// If active text field is hidden by keyboard, scroll it so it's visible
// Your application might not need or want this behavior.
CGRect aRect = self.view.frame;
aRect.size.height -= kbSize.height;
if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height);
[scrollView setContentOffset:scrollPoint animated:YES];
}
}
Again, I'm taking this code directly from the IOS Programming guide in the link. The UI layout looks like a basic chat window. I would like to move the "input" field up while the soft keyboard is visible. Thanks!
Update
It seems that I needed to add some padding to actually see the controls located at the bottom of the scrollview.
CGPoint scrollPoint = CGPointMake(0.0, (activeField.frame.origin.y - kbSize.height) + 10.0);
How come I don't see scrollbars?
IF YOU HAVE PARENT VIEW AS SCROLL THEN JUST USE : UITextFielddelegate and set the methods
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
if(textField == self.txtUserName)
{
[self.txtPassword becomeFirstResponder];
}
else if(textField == self.txtPassword){
[self.txtPassword resignFirstResponder];
CGPoint bottomOffset = CGPointMake(0, 0);
[scrView setContentOffset:bottomOffset animated:YES];
[textField resignFirstResponder];
}
return YES;
}
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if (textField == self.txtUserName)
{
CGPoint bottomOffset = CGPointMake(0, 80);
[scrView setContentOffset:bottomOffset animated:YES];
}
if (textField == self.txtPassword)
{
CGPoint bottomOffset = CGPointMake(0, 135);
[scrView setContentOffset:bottomOffset animated:YES];
}
}
Scrollbars should show up only on user interaction. That's not the case here since your programmatically setting the inset of your scrollview.
If you want to show the scroll bars, i believe UIScrollView defines a flashScrollIndicators method, that should show the scroll bars.