I have a UILabel I want rotated 180 degrees, so the text appears upside down. I did some searching and found this question that covers rotations with CGAffineTransformMakeRotation.
Is there a way to rotate it right on my UIStoryboard? The label is always going to appear with that same fixed rotation, so it would be nice to have it just show up the same way in my storyboard as it does when running. I found this post saying it's impossible, but that's a pretty old post (I think pre-storyboard). Of course if it's not with a storyboard, I can just use CGAffineTransformMakeRotation like in the other question I linked in my viewDidLoad.
No, there's no way to do that; you have to apply the transform in code (and, as you say, viewDidLoad is a good place).
The storyboard/nib editor is remarkably good, but there is still a lot of stuff you can do to a view that you can do only in code, and that applies to specific UIView subclasses as well. And of course you can't access a view's underlying layer in the storyboard/nib editor at all either; for example, you can't give the view rounded corners in the storyboard/nib.
Related
If you came to this question based on the title but are not interested in Mongolian, you might be looking for this Q&A instead:
Swift: How can you rotate text for UIButton and UILabel?
I've been learning Swift in order to develop iOS apps for traditional Mongolian. The problem is that traditional Mongolian is written vertically from top to bottom and from left to right. My question is how do I display text vertically and still have line wrapping work?
If you stay with me for a minute, I'll try to explain the problem more clearly. Below is an image of the kind of text view I would like to achieve. In case the foreign script throws you off, I have included English text that follows the same pattern. In fact, if the app has any English text to display, this is how it should look.
For a simple one-line UILabel, a 90 degree clockwise rotation would work. However, for a multi-line UITextView I need to deal with line wrapping. If I just do a plain 90 degree rotation, the first thing written will end up being on the last line.
So far I have made a plan that I think can overcome this problem:
Make a custom font in which all of the letters are mirrored vertically.
Rotate the text view 90 degrees clockwise.
Mirror the text view horizontally.
That should take care of the text wrap.
I can do the mirrored font. However, I don't know how to do the Swift coding for the rotation and mirroring of the UITextView. I've found the following links that seem to give hints to parts of the solution, but they are all in Objective C and not in Swift.
How to rotate sub-views around their own centres?
Rotate UIView around its center keeping its size
iOS: Mirror content on screen
Mirroring UIView
There are traditional Mongolian apps in the app store (like this and this) but I haven't found anyone yet who is sharing their source code, so I don't know what they are doing behind the scenes to display the text. I plan to make my code open source so that it is not so hard for others in the future to develop apps for the several million people who read traditional Mongolian. Any assistance you can give to this endeavor would be much appreciated, not just by me but also by the Mongolian people. Even if you don't know yourself, upvoting this question to make it more visible would help.
Update
#sangonz's answer is still a great answer, but I temporarily unmarked it as the accepted answer because I just couldn't get everything to work. Specifically:
Enabling scrolling (either by embeding the custom view in a scrollview or by subclassing UIScrollView). In the github project, #sangonz said this should be easy, but it wasn't for me.
Getting a relayout (rather than stretching) of the word lines on an orientation change. I think this shouldn't be too hard to solve with a little more research.
Why don't the text lines go all the way to the edge of the view? There is a big gap at the bottom.
How to unlink the NSTextStorage of the custom vertical view from the other UITextView. (see this question)
Up to this point I have been using the original method I proposed above, but what I really want is to get something like what #sangonz proposed working.
I am also now considering alternate methods like
Using Core Text, Disadvantage: it feels like reinventing the wheel
Using WebKit, Disadvantage: Apple no longer uses WebKit for their UITextView
Edit: This is how I finally did it.
Here's a very basic implementation in my GitHub: Vertical-Text-iOS.
Nothing fancy, but it works. Finally I had to mix TextKit and image processing. Take a look at the code. It involves:
Subclassing NSTextContainer to get the right text dimensions.
Creating a custom UIView to render the text applying affine transformations to each line and rendering using NSLayoutManager to keep all TextKit features.
TextKit way
The proper way to keep all native text benefits (e.g. highlighting, selection...) is to use standard TextKit APIs. The method you are proposing would break all that or would possibly result in strange behaviour.
However, looks like TextKit in iOS does not support vertical orientation out-of-the-box yet, but it is prepared for that. As a side note, in OS X it is somewhat supported and you could call textView.setLayoutOrientation(.Vertical), but it still has some limitations.
The NSTextLayoutOrientationProvider protocol defines an interface
providing the default orientation for text laid out in a conforming
object, in absence of an explicit NSVerticalGlyphFormAttributeName
attribute. The only UIKit class that implements this interface is
NSTextContainer, whose default implementation returns
NSTextLayoutOrientationHorizontal. An NSTextContainer subclass that
handles vertical text could set this property to
NSTextLayoutOrientationVertical to support the custom layout
orientation logic.
Source: UIKit > NSTextLayoutOrientationProvider Protocol Reference for iOS
In conclusion, you should start subclassing NSTextContainer, and you will have to deal with NSLayoutManager and NSTextContainer a lot.
Custom image processing way
If, on the other hand you decide to follow your custom text rendering I suggest the following approach.
Render the normal text to a hidden layer with a normal font. Give it the correct size to its bounding box.
Get the text properties, mainly text height and line spacing.
Process the image drawing each line in reverse order from bottom to top as you can see in the image below. You should get a new CGImage as a result.
Rotate the image creating a UIImage and setting the correct UIImageOrientation value.
Insert that image into a UIScrollView that only allows horizontal scrolling.
Beware this method renders the whole text, so don't use it for very long texts. If you need to do that, you will need to consider a tiling approach. Watch WWDC 2013 > 217 - Exploring Scroll Views on iOS 7.
Good luck!
Update: (image from github project)
If you're going to rotate the text I would suggest using a right-to-left layout so that you can skip the mirroring step (and just rotate the other way).
You should be able to just set the label/textview's transform property:
view.transform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(CGFloat(-M_PI_2)), view.bounds.width, view.bounds.height)
You need to translate after you rotate because the view rotates around its origin (in the upper left).
The good news is that gestures and taps are transformed at the same time the pixels are, so controls continue to work the way you expect them to.
I have a UIViewController that overlays controls on a view presenting what the camera sees. I have a couple of scenarios I would like to allow.
For the iPad, I want to keep the controls on the right most edge of the device, by your right thumb, no matter what the device's rotation. The controls should rotate their content so that their top is always upwards (away from the ground). I don't want the camera view to rotate at all, because that would just be silly – its position & size should stay the same and its contents shouldn't rotate either.
For the iPhones, I want to keep the controls at the bottom of the device's screen, by to the home button, wherever the home button actually is. The controls should rotate their content so that up is always pointing upwards. Again, I don't want the camera view's frame or content to take part in any view rotation animation at all.
I'm using auto-layout.
I'm wondering if there is any way to describe some or all of this in a storyboard. In particular, it'd be great to be able to describe that some view positions need to autorotate (ie, the controls, on iPad), but that other views don't (the camera view).
A question from 2011 indicates this wasn't possible at the time, but perhaps things have moved on since then? If it's not directly supported, can you suggest an approach and are there some sensible places to be hooking in to autorotation to achieve this?
Ok, this isn't quite a complete answer, but I tried a few things which look promising.
First, you can create a separate set of constraints for portrait vs. landscape using the size specifiers: landscape is w Regular, h Any; portrait is w Any, h Regular (I think -- double-check these) This is accessible via the pop-up control in the bottom-center of the storyboard view. By installing different constraints for portrait and landscape, it should be possible to scale the width and height of your controls' container view so it appears to be in a constant position w.r.t. device orientation; in other words, the container doesn't actually counter-rotate -- it scales so it effectively looks like it has counter-rotated.
I got this close to working. It looks like it's doing the correct thing in the storyboard view, but when I actually run it, I get debug messages about conflicting constraints. Not sure how to fix this, but maybe play with the constraint priorities? That sometimes helps.
A second thing I (partially) tried was creating a custom container view class which counter-rotates itself to the correct position based on the device orientation (in the UIDevice class). You implement this by overriding layoutSubviews. For each orientation, you define a transform which puts it in the correct position, and set the view's transform property.
Another possible solution is to override updateConstraints in your view controller and add/remove constraints to position/scale your container to the correct place for each orientation.
For all of these, the idea is that you "force" the container to be in the correct place, but leave the subviews (the actual controls) alone. The controls should do the right thing if their constraints are independent of the specific orientation of the container view.
So, those are some ideas anyway... if they lead you to an actual solution, could you post it? I anticipate having a need for this myself.
I have a VC with two imageViews. One takes up the whole screen, the other is actually slightly larger and can be moved around on the screen (Pan Gesture).
When the device changes orientation only the static view changes orientation, or just updates its frame maybe (automatically).
The second Pan UIImageView does not move. Is there a reason for this, or a switch to make this happen somewhere possibly in the storyboard properties of the UIImageView itself? I would like the view to move too.
I understand that an option would be to use the methods that confirm the orientation has completed a change and transform the imageView myself, which is not a problem... but checking I am not missing something that could do this for me automatically first.
Ensure you are using correct AutoResizeMasks on the imageview. Provide your code or storyboard description for a more complete answer.
-Additionally if you are using AutoLayout, you may need to add the appropriate constraints (making it slightly larger than the view may confuse it as to your intention without proper suggestions via constraints).
I've been looking around here and seeing lots of questions that seem related, but I haven't found anything that's exactly what I need.
I have three UIViews that are animating inside a custom viewController. I want there to be a static background inside the viewController that is only revealed within the clipping bounds of the UIViews.
It's kind of like spotlights on a prison yard: the background should stay in one place, but as the spotlights move you can see different parts of it. Except that I don't need circles, regular old UIView frames are fine.
For instance, it seems like what I need is in the answer to this question: How to achieve dynamic UIView masking?
...the answerer provides a link to a page that shows how to statically mask an image, and then the answerer says "But personally I think i would make 2 UIImage views and crop the content of the draggable UIView"--without any info on how to do that.
How do you do that?
iPad app; I'm trying to resize my view when the keyboard appears. It amounts to calling this code at appropriate times:
CGRect adjustedFrame = self.frame;
adjustedFrame.size.height -= keyboardFrame.size.height;
[self setFrame:adjustedFrame];
Using this technique for a view contained in a uisplitview-based app works in all 4 orientations, but I've since discovered that a vanilla uiview-based app does not work.
What happens is that apparently the uisplitview is smart enough to convert the coordinates of its subviews (their frame) such that the origin is in the "viewer's top left" regardless of the orientation. However, a uiview is not able to correctly report these coordinates. Though the origin is reported as (0,0) in all orientations, the view's effective origin is always as if the ipad were upright.
What is weird about this is that the view correctly rotates and draws, but it always originates in the literal device top left. How can I get the view to correctly make its origin the "top left" to the viewer, not the device's fixed top left? What am I missing? Please, for something so trivial I've spent about 6 hours on this already with every brute force technique and research angle I could think of.
This is the original source which doesn't work in this case:
move up UIToolbar
OK, I don't know what the ACTUAL answer is to the original question, but I can say with certainty that one way to resolve the issue is to always ensure that you don't manipulate a viewController's view directly. Always wrap your view inside a container view inside the main "view", then have that container view adjust its position etc as needed. Works exactly as the splitview does, probably because in both cases now the view in question is a subview of the main "view". What a relief!