UINavigationBar multi-line title - uinavigationcontroller

Is there a straightforward way of overriding the titleView of the current navigation bar item in a navigation bar within a navigation controller? I've tried creating a new UIView and replacing the titleView property of topView with my own UIVIew with no success.
Basically, I want a multi-line title for the navigation bar title. Any suggestions?

Set the titleView property of the UINavigationItem. For example, in the view controller's viewDidLoad method you could do something like:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 480, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = [UIFont boldSystemFontOfSize: 14.0f];
label.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.5];
label.textAlignment = UITextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.text = #"This is a\nmultiline string";
self.navigationItem.titleView = label;
#if !__has_feature(objc_arc)
[label release];
#endif
It shows up like this:
Remember the titleView property is ignored if leftBarButtonItem is not nil.

for Swift:
let label = UILabel(frame: CGRectMake(0, 0, UIScreen.main.bounds.width, 44))
label.backgroundColor = UIColor.clearColor()
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.Center
label.text = "multiline string"
self.navigationItem.titleView = label
for swift 4:
let label = UILabel(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: 44.0))
label.backgroundColor = UIColor.clear
label.numberOfLines = 0
label.textAlignment = NSTextAlignment.center
label.text = "first line\nsecond line"
self.navigationItem.titleView = label

Swift solution:
2 lines in NavigationBar:
private func setupTitleView() {
let topText = NSLocalizedString("key", comment: "")
let bottomText = NSLocalizedString("key", comment: "")
let titleParameters = [NSForegroundColorAttributeName : UIColor.<Color>(),
NSFontAttributeName : UIFont.<Font>]
let subtitleParameters = [NSForegroundColorAttributeName : UIColor.<Color>(),
NSFontAttributeName : UIFont.<Font>]
let title:NSMutableAttributedString = NSMutableAttributedString(string: topText, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: bottomText, attributes: subtitleParameters)
title.appendAttributedString(NSAttributedString(string: "\n"))
title.appendAttributedString(subtitle)
let size = title.size()
let width = size.width
guard let height = navigationController?.navigationBar.frame.size.height else {return}
let titleLabel = UILabel(frame: CGRectMake(0,0, width, height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .Center
navigationItem.titleView = titleLabel
}
2 line in BarButton
let string = NSLocalizedString("key", comment: "")
let attributes = [NSForegroundColorAttributeName : UIColor.<Color>,
NSFontAttributeName : UIFont.<Font>]
let size = (string as NSString).sizeWithAttributes(attributes)
guard let height = navigationController?.navigationBar.frame.size.height else {return}
let button:UIButton = UIButton(frame: CGRectMake(0, 0, size.width, height))
button.setAttributedTitle(NSAttributedString(string: string, attributes: attributes), forState: .Normal)
button.addTarget(self, action: #selector(<SELECTOR>), forControlEvents: .TouchUpInside)
button.titleLabel?.numberOfLines = 0
button.titleLabel?.textAlignment = .Right
let rightBarButton = UIBarButtonItem(customView: button)
navigationItem.rightBarButtonItem = rightBarButton
result -

After a lot of tweaking, I still couldn't get petert's solution to work for me in iOS 8. Here's a copy-paste-able solution for iOS 8/9. Credit goes to Matt Curtis's github post
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if(!self.navigationItem.titleView){
self.navigationItem.titleView = ({
UILabel *titleView = [UILabel new];
titleView.numberOfLines = 0;
titleView.textAlignment = NSTextAlignmentCenter;
titleView.attributedText = [[NSAttributedString alloc] initWithString:#"2\nLINES" attributes:
self.navigationController.navigationBar.titleTextAttributes
];
[titleView sizeToFit];
// You'll need to set your frame otherwise if your line breaks aren't explcit.
titleView;
});
}
}

What to do when label is not centered
If you encounter same issue as me - that label is not centered in navigationItem because of back button, embed your UILabel to UIView. UILabel is then not forced to grow with it's text, but stop growing when it's width raise view's width. More about this issue you can find here: Can't set titleView in the center of navigation bar because back button ( Darren's answer )
Not centered:
- (void)setTwoLineTitle:(NSString *)titleText color:(UIColor *)color font:(UIFont *)font {
CGFloat titleLabelWidth = [UIScreen mainScreen].bounds.size.width/2;
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleLabelWidth, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = font;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.textColor = color;
label.text = titleText;
self.navigationItem.titleView = label;
}
Centered:
- (void)setTwoLineTitle:(NSString *)titleText color:(UIColor *)color font:(UIFont *)font {
CGFloat titleLabelWidth = [UIScreen mainScreen].bounds.size.width/2;
UIView *wrapperView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, titleLabelWidth, 44)];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, titleLabelWidth, 44)];
label.backgroundColor = [UIColor clearColor];
label.numberOfLines = 2;
label.font = font;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.textColor = color;
label.text = titleText;
[wrapperView addSubview:label];
self.navigationItem.titleView = wrapperView;
}

Here is a Swift 3 version of handling a multiline title:
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 44))
label.backgroundColor = .clear
label.numberOfLines = 0
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 14.0)
label.text = "This is a Multi-Line title of UINavigationBar"
self.navigationItem.titleView = label
}

Here's a Swift 4 way of doing it-
let upperTitle = NSMutableAttributedString(string: "\(text1)", attributes: [NSAttributedStringKey.font: UIFont(name: "SFProDisplay-Heavy", size: 17)!])
let lowerTitle = NSMutableAttributedString(string: "\n\((text2)!)", attributes: [NSAttributedStringKey.font: UIFont(name: "SFProText-Light", size: 11)! , NSAttributedStringKey.foregroundColor: UIColor(hex: "#607D8B")])
upperTitle.append(lowerTitle)
let label1 = UILabel(frame: CGRect(x: 0, y: 0, width: 400, height:44))
label1.numberOfLines = 0
label1.textAlignment = .center
label1.attributedText = upperTitle //assign it to attributedText instead of text
self.navigationItem.titleView = label1

Swift 4
extension UINavigationItem {
#objc func setTwoLineTitle(lineOne: String, lineTwo: String) {
let titleParameters = [NSAttributedStringKey.foregroundColor : UIColor.white,
NSAttributedStringKey.font : UIFont.boldSystemFont(ofSize: 17)] as [NSAttributedStringKey : Any]
let subtitleParameters = [NSAttributedStringKey.foregroundColor : UIColor.flatWhite(),
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 12)] as [NSAttributedStringKey : Any]
let title:NSMutableAttributedString = NSMutableAttributedString(string: lineOne, attributes: titleParameters)
let subtitle:NSAttributedString = NSAttributedString(string: lineTwo, attributes: subtitleParameters)
title.append(NSAttributedString(string: "\n"))
title.append(subtitle)
let size = title.size()
let width = size.width
let height = CGFloat(44)
let titleLabel = UILabel(frame: CGRect.init(x: 0, y: 0, width: width, height: height))
titleLabel.attributedText = title
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .center
titleView = titleLabel
}
}
Font, color, and navigation bar height are hardcoded here.

In Swift 5,
let wrapperView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width * 0.75, height: 44))
let label = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width * 0.75, height: 44))
label.backgroundColor = .clear
label.numberOfLines = 2
label.font = UIFont.boldSystemFont(ofSize: 16.0)
label.textAlignment = .center
label.textColor = .white
label.text = "multi line text"
wrapperView.addSubview(label)
self.navigationItem.titleView = wrapperView

Most of the solutions, except the one from #gbk, use hardcoded height 44pt for the UIView (wrapper view) and UILabel. All are created by codes. I overlooked #gbk solution which dynamically read height of navigation bar.
I ran into problem when orientation = landscape on iOS 11 (iPhone 5s). The label's height won't adjust and when I set one line of text for landscape, the text align to bottom of navigation bar.
Somehow I found that I can add the UILabel in Storyboard and create an IBOutlet for that. Isn't that nicer?
Add an UIView to navigation bar in storyboard. When dragging it over navigation bar, it will appears as a blue box. If a vertical stroke appears, you are adding it to left/right bar button items array. Note: there can only be ONE UIView. If you add it correctly, it will appear under Navigation Item on the scene panel (on left).
Drag a UILabel into this UIView.
Since UIView will have NO SIZE but centralized in navigation bar, you cannot add four zero's constraint. Just add two constraints to the UILabel so it position in the center of superview: Align Center X and Y to Superview.
Configure UILabel as usual. For multiple line, I set number of lines to zero (0).
Create an IBOutlet on your view controller and you can use it as usual. To have different size of text, use attribute string (lots of solutions above).
I tested on iPhone 5s with iOS 11.2.6 and the text just position in center with no problem, work fine on portrait and landscape.

Swift 5+ https://stackoverflow.com/a/68739808/6881070
very easy and smooth solution in one function link is mention

Related

Corner Radius Not Working?

im trying to setup a circle image view and when I set the corner radius to perform the operation it does absolutely nothing. I've looked at various threads and solutions none worked
import UIKit
class AlterProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view?.backgroundColor = UIColor.white
navigationItem.title = "Profile Settings"
view.addSubview(selectProfileImage)
///Constraints for all views will go here
_ = selectProfileImage.anchor(view.centerYAnchor, left: view.leftAnchor, bottom: nil, right: nil, topConstant: -275, leftConstant: 135, bottomConstant: 0, rightConstant: 0, widthConstant: 100, heightConstant: 100)
// selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width/2
///////////////////////////////////////////////
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//Where all buttons and labels will be added
//will just be a nice looking image view to be next to the profile settings button
lazy var selectProfileImage: UIImageView = {
let selectPicture = UIImageView()
// self.selectProfileImage.layer.cornerRadius = self.selectProfileImage.frame.size.width / 2;
selectPicture.image = UIImage(named: "Paris")
// selectPicture.layer.cornerRadius = selectPicture.frame.size.width / 2;
selectPicture.clipsToBounds = true
selectPicture.translatesAutoresizingMaskIntoConstraints = false
selectPicture.contentMode = .scaleAspectFill
selectPicture.layer.shouldRasterize = true
selectPicture.layer.masksToBounds = true
return selectPicture
}()
///////////////////////////////////////////////////////////////////////////////////
}
None of the methods seem to work im actually kind of stumped right now
Given that you layout with AutoLayout I would suspect the image view simply doesn't have the correct size when you calculate the radius. The image view is initialized with a size of 0,0 and thus the calculated radius will be 0 as well. Instead, move the radius calculation in viewDidLayoutSubviews after calling super:
func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width / 2;
selectProfileImage.layer.masksToBounds = true
}

Change border color of buttons once pressed

I am trying to have the borders for the buttons change once buttons are pressed, here is how the code currently looks:
[m_btnEthinic addObject:btnEthnicity1];
[m_btnEthinic addObject:btnEthnicity2];
[m_btnEthinic addObject:btnEthnicity3];
[m_btnEthinic addObject:btnEthnicity4];
[m_btnEthinic addObject:btnEthnicity5];
[m_btnEthinic addObject:btnEthnicity6];
[m_btnEthinic addObject:btnEthnicity7];
[m_btnEthinic addObject:btnEthnicity8];
[m_btnEthinic addObject:btnEthnicity9];
for (UIButton* btn in m_btnEthinic) {
btn.layer.borderWidth = 1;
btn.layer.cornerRadius = 13;
btn.layer.borderColor = COLOR_GRAYBORDERBTN.CGColor;
[btn setBackgroundColor: COLOR_PURP];
[btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]
With storyboard, you can do like this:
drag and place the button into viewcontroller
set the button Tag as per the buttons
drag the buttons into .h and create IBOutletCollection NSArray for buttons. Link all buttons into same outlet.
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *btn;
set IBAction common for all the buttons.
- (IBAction)btnActions:(UIButton *)sender;
once created IBAction, btnActions: method will be created in .m
-(IBAction) btnActions: (UIButton * ) sender {
for (UIButton * b in self.btn) {
//checking if already have borders for the buttons
if (b.layer.cornerRadius == 13) {
b.layer.borderWidth = 0;
b.layer.cornerRadius = 0;
}
//setting the borders for the selected button
if (b.tag == sender.tag) {
b.layer.borderWidth = 2;
b.layer.cornerRadius = 13;
b.layer.borderColor = [UIColor yellowColor].CGColor;
}
}
}
You can add a selector to you button, and use a BOOL property to track the button status and witch the color in the selector. If you have an array of buttons you will need an array of status flags. The code below should work (sorry for any typo. I am not in front of my computer at this time).
#property (strong, nonatomic) borderStatus;
-(void)viewDidLoad {
borderStatus = NO;
[btn addTarget:self action:#selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
-(void)buttonPressed:(id)sender {
if(borderStatus){
((UIButton*)sender).layer.borderColor = [UIColor redColor];
borderStatus = NO;
} else {
((UIButton*)sender).layer.borderColor = [UIColor greenColor];
borderStatus = YES;
}
}
will, I guess you maybe need this. You can add two events for the buttons. So set border when the event UIControlEventTouchDown response, and recover it when the event UIControlEventTouchUpInsideresponse.
All this on my Github name 'T_yunButtonBorder' git#github.com:xmy0010/DemoForCSDN.git
Like this:
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *m_btnEthinic = #[].mutableCopy;
for (int i = 0; i < 8; i++) {
UIButton *btnEthnicity = [UIButton buttonWithType:UIButtonTypeCustom];
[btnEthnicity setTitle:[NSString stringWithFormat:#"btn%d", i] forState:UIControlStateNormal];
btnEthnicity.frame = CGRectMake(50, 50 + 40 * i, 50, 20);
btnEthnicity.backgroundColor = [UIColor redColor];
[btnEthnicity addTarget:self action:#selector(btnEthnicityTouchDown:) forControlEvents: UIControlEventTouchDown];
[btnEthnicity addTarget:self action:#selector(btnEthnicityTouchUp:) forControlEvents: UIControlEventTouchUpInside];
[self.view addSubview:btnEthnicity];
[m_btnEthinic addObject:btnEthnicity];
}
}
- (void)btnEthnicityTouchDown:(UIButton *)sender {
NSLog(#"down");
sender.layer.borderWidth = 2;
sender.layer.cornerRadius = 10;
sender.layer.borderColor = [UIColor greenColor].CGColor;
}
- (void)btnEthnicityTouchUp:(UIButton *)sender {
NSLog(#"up");
sender.layer.borderWidth = 0;
sender.layer.cornerRadius = 0;
}

Buttons and textboxes not showing text

I'm trying to create a tile with 3 pages, each page contain a textbox and a button. Here is my code
[self.client.personalizationManager
themeWithCompletionHandler:^(MSBTheme *theme, NSError *error){
if (error){
// handle error
NSLog(#"Error: %#", error);
}
NSLog(#"Creating tile...");
NSString *tileName = #"SnapBand";
// Create Tile Icon
MSBIcon *tileIcon = [MSBIcon iconWithUIImage:[UIImage imageNamed:#"Band46x46.png"] error:nil];
// Create small Icon
MSBIcon *smallIcon = [MSBIcon iconWithUIImage:[UIImage imageNamed:#"Band24x24.png"] error:nil];
// Create a Tile
NSUUID *tileID = [[NSUUID alloc] initWithUUIDString:#"6120f0c7-fec4-44b0-99fa-04c7ca6cf869"];
MSBTile *tile = [MSBTile tileWithId:tileID name:tileName tileIcon:tileIcon smallIcon:smallIcon error:nil];
// Create a Picture Text Block
MSBPageTextBlock *pictureTextBlock = [self createMSBandTextBlockWithParameters:10 WithTheme:theme];
// Create a Picture Snap Button
MSBPageTextButton *pictureButton = [self createMSBandActioButtonWithParameters:11 WithTheme:theme];
// Create a Movie Text Block
MSBPageTextBlock *movieTextBlock = [self createMSBandTextBlockWithParameters:20 WithTheme:theme];
// Create a Movie Snap Button
MSBPageTextButton *movieButton = [self createMSBandActioButtonWithParameters:21 WithTheme:theme];
// Create a Brust Text Block
MSBPageTextBlock *brustTextBlock = [self createMSBandTextBlockWithParameters:30 WithTheme:theme];
// Create a Brust Snap Button
MSBPageTextButton *brustButton = [self createMSBandActioButtonWithParameters:31 WithTheme:theme];
MSBPageFlowPanel *pictureFlowPanel = [[MSBPageFlowPanel alloc] initWithRect:[MSBPageRect rectWithX:0 y:0 width:245 height:105]];
MSBPageFlowPanel *movieFlowPanel = [[MSBPageFlowPanel alloc] initWithRect:[MSBPageRect rectWithX:245 y:0 width:245 height:105]];
MSBPageFlowPanel *brustFlowPanel = [[MSBPageFlowPanel alloc] initWithRect:[MSBPageRect rectWithX:490 y:0 width:245 height:105]];
[pictureFlowPanel addElements:#[pictureTextBlock, pictureButton]];
[movieFlowPanel addElements:#[movieTextBlock, movieButton]];
[brustFlowPanel addElements:#[brustTextBlock, brustButton]];
MSBPageScrollFlowPanel *panel = [[MSBPageScrollFlowPanel alloc] initWithRect:[MSBPageRect rectWithX:0 y:0 width:245 height:105]];
panel.horizontalAlignment = MSBPageHorizontalAlignmentLeft;
panel.verticalAlignment = MSBPageVerticalAlignmentTop;
panel.orientation = MSBPageFlowPanelOrientationHorizontal;
[panel addElements:#[pictureFlowPanel, movieFlowPanel, brustButton]];
MSBPageLayout *layout = [[MSBPageLayout alloc] init];
layout.root = panel;
[tile.pageLayouts addObject:layout];
[self.client.tileManager addTile:tile completionHandler:^(NSError *error) {
if (!error || error.code == MSBErrorTypeTileAlreadyExist)
{
NSLog(#"Creating page...");
NSUUID *pageID = [[NSUUID alloc] initWithUUIDString:#"a234e9ba-1c09-46c3-b3b3-12bb1c9cf90f"];
NSArray *pageValues = #[[MSBPageTextButtonData pageTextButtonDataWithElementId:11 text:#"Snap!" error:nil],
[MSBPageTextBlockData pageTextBlockDataWithElementId:10 text:#"Picture" error:nil]];
MSBPageData *pageDataPicture = [MSBPageData pageDataWithId:pageID layoutIndex:0 value:pageValues];
NSUUID *pageIDMovie = [[NSUUID alloc] initWithUUIDString:#"1535d78b-9784-4892-a94c-030df63de453"];
NSArray *pageValuesMovie = #[[MSBPageTextButtonData pageTextButtonDataWithElementId:21 text:#"Snap!" error:nil],
[MSBPageTextBlockData pageTextBlockDataWithElementId:20 text:#"Movie" error:nil]];
MSBPageData *pageDataMovie = [MSBPageData pageDataWithId:pageIDMovie layoutIndex:1 value:pageValuesMovie];
NSUUID *pageIDBrust = [[NSUUID alloc] initWithUUIDString:#"1fd74789-3936-4324-a1fc-3e1d66d6e189"];
NSArray *pageValuesBrust = #[[MSBPageTextButtonData pageTextButtonDataWithElementId:31 text:#"Snap!" error:nil],
[MSBPageTextBlockData pageTextBlockDataWithElementId:30 text:#"Brust" error:nil]];
MSBPageData *pageDataBrust = [MSBPageData pageDataWithId:pageIDBrust layoutIndex:2 value:pageValuesBrust];
[self.client.tileManager setPages:#[pageDataPicture, pageDataMovie, pageDataBrust] tileId:tile.tileId completionHandler:^(NSError *error) {
if (!error)
{
NSLog(#"Completed band tile creation");
[self.snapPictureButton setBackgroundImage:[UIImage imageNamed:#"GreenStatus"] forState:UIControlStateNormal];
[self sendHapticFeedback];
}
else
{
NSLog(#"Error: %#", error.localizedDescription);
}
}];
}
else
{
NSLog(#"Error: %#", error.localizedDescription);
}
}];
}];
I created a constructor method to generate textboxes and buttons, the methods are:
-(MSBPageTextBlock *) createMSBandTextBlockWithParameters:(int)textBoxID WithTheme:(MSBTheme *) theme{
MSBPageTextBlock *textBlock = [[MSBPageTextBlock alloc] initWithRect:[MSBPageRect rectWithX:0 y:0 width:200 height:40] font:MSBPageTextBlockFontSmall];
textBlock.elementId = textBoxID;
textBlock.baselineAlignment = MSBPageTextBlockBaselineAlignmentRelative;
textBlock.horizontalAlignment = MSBPageHorizontalAlignmentLeft;
textBlock.autoWidth = YES;
textBlock.color = theme.baseColor;
textBlock.margins = [MSBPageMargins marginsWithLeft:15 top:0 right:0 bottom:0];
return textBlock;
}
and
-(MSBPageTextButton *) createMSBandActioButtonWithParameters:(int)textBoxID WithTheme:(MSBTheme *) theme{
MSBPageTextButton *actionButton = [[MSBPageTextButton alloc] initWithRect:[MSBPageRect rectWithX:0 y:0 width:200 height:40]];
actionButton.elementId = textBoxID;
actionButton.horizontalAlignment = MSBPageHorizontalAlignmentCenter;
actionButton.pressedColor = theme.baseColor;
actionButton.margins = [MSBPageMargins marginsWithLeft:15 top:0 right:0 bottom:0];
return actionButton;
}
The buttons and the textboxes doesn't show any text on the band, i can see the buttons on my band, the position of the third button is not correct, and there is no text in two of the buttons.
Can anyone help me?
Looks like you have created one layout with a horizontal scroll panel. And that panel has pictureFlowPanel, movieFlowPanel, brustButton as its elements.
So when you create pages you will just need to create one page with the above layout. However, in your code I see you are trying to create three pages with layout index 0, 1 and 2. Only layout 0 is valid and layouts 1 and 2 do not exist.
One possible fix is to break apart the layouts into three separate layouts:
MSBPageLayout *pictureLayout = [[MSBPageLayout alloc] init];
pictureLayout.root = pictureFlowPanel;
[tile.pageLayouts addObject:pictureLayout]; //Layout 0
MSBPageLayout *movieLayout = [[MSBPageLayout alloc] init];
movieLayout.root = movieFlowPanel;
[tile.pageLayouts addObject:movieLayout]; //Layout 1
MSBPageLayout *burstLayout = [[MSBPageLayout alloc] init];
burstLayout.root = burstFlowPanel;
[tile.pageLayouts addObject:burstLayout]; //Layout 2

ios 7 view with transparent content overlaps previous view

Recently I updated my xcode project to work with iOS 7, but i faced a big problem. Because my whole application has only one background image (UIImageView added to key window) and all views are transparent, I face a problem when pushing UIViewController, because pushed view controller overlaps previous view (you can see it in the picture here: http://grab.by/qp0k). I can predict that this is because in iOS 7 push transition has been changed, because now it slides half a screen. Maybe anyone knows how to fix this issue?
This is how I set my key windows
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIImageView *background = [[UIImageView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
background.image = [UIImage imageNamed:#"background.png"];
UINavigationController *navi = [[UINavigationController alloc]initWithRootViewController:self.viewController];
self.window.rootViewContro‌​ller = navi;
[self.window makeKeyAndVisible];
Afterwards when user clicks on "start workout" button I push my next view as always:
workoutView *w = [[workoutView alloc]initWithNibName:#"workoutView" bundle:nil];
[self.navigationController pushViewController:w animated:YES];
I did this.
-(void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
[self.view setAlpha:0];
}
Do not forget re set alpha when come back.
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.view setAlpha:1];
}
I solved the problem by implementing the new UINavigationControllerDelegate Method animationControllerForOperation.
For example:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
PushTransition* transition = [PushTransition new];
[transition setNavigationControllerOperation: operation];
return transition;
}
PushTransition is a class that implements the UIViewControllerAnimatedTransitioning protocol and the two methods transitionDuration and animateTransition from that protocol. Additionally, i have added a property to pass the operation (tells me if it is a push or pop transition).
Just put the animation code for moving the views into the animateTransition as follows:
// the containerView is the superview during the animation process.
UIView *container = transitionContext.containerView;
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *fromView = fromVC.view;
UIView *toView = toVC.view;
CGFloat containerWidth = container.frame.size.width;
// Set the needed frames to animate.
CGRect toInitialFrame = [container frame];
CGRect fromDestinationFrame = fromView.frame;
if ([self navigationControllerOperation] == UINavigationControllerOperationPush)
{
toInitialFrame.origin.x = containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = -containerWidth;
}
else if ([self navigationControllerOperation] == UINavigationControllerOperationPop)
{
toInitialFrame.origin.x = -containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = containerWidth;
}
// Create a screenshot of the toView.
UIView *move = [toView snapshotViewAfterScreenUpdates:YES];
move.frame = toView.frame;
[container addSubview:move];
[UIView animateWithDuration:TRANSITION_DURATION delay:0
usingSpringWithDamping:1000 initialSpringVelocity:1
options:0 animations:^{
move.frame = container.frame;
fromView.frame = fromDestinationFrame;
}
completion:^(BOOL finished) {
if (![[container subviews] containsObject:toView])
{
[container addSubview:toView];
}
toView.frame = container.frame;
[fromView removeFromSuperview];
[move removeFromSuperview];
[transitionContext completeTransition: YES];
}];
described it and you can you are done. Additionally you can make any push or pop animation you like.
I fixed it by doing this when initialising the view:
self.view.clipsToBounds = YES;
You might want to look into a new iOS7 feature that allows you to define your own custom UIViewController transitions. Look in the docs for UIViewControllerTransitioningDelegate. Also, here's a link to an article about it: http://www.doubleencore.com/2013/09/ios-7-custom-transitions/
Ah, now I understand the issue. You were right, seems to be caused by the previous UIViewController not being hidden after the transition (because of the new transition effect).
There doesn't seem to be any SDK method to control this behavior. Short of redesigning the app to not requiring the background be static, you'll probably have to roll your own navigation. OSNavigationController is a complete reimplementation of UINavigationController that might help you out. If they haven't updated to the iOS 7 transition, you'll probably be good to go. If they have you can always use an older version.
I had the same problem. Try to load your background image in the init method. For me, it worked (sometimes):
For example:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
self.view.backgroundColor = [UIColor whiteColor];
[self.imageBack setImage:[UIImage imageNamed:#"mayBack.png"]];
}
return self;
}
However, you could see glimpses..
The best solution I found, beside implementing the new iOS7 transition protocol, is to implement a category, and use that category whenever you need it.
You can find the answer here
Setting the image to the background color solved the issue:
self.view.backgroundColor =
[UIColor colorWithPatternImage:[UIImage imageNamed:#"mainback.png"]];
Take a look at the UINavigationController category in that post (it solved me problem) :
https://stackoverflow.com/a/18882232/2826409
Props to #snoersnoer.
Here is the code in Swift 3.
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let pushTransition = SUPushTransition()
pushTransition.navigationControllerOperation = operation
return pushTransition
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
// the containerView is the superview during the animation process.
let container = transitionContext.containerView
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
let toVC = transitionContext.viewController(forKey:UITransitionContextViewControllerKey.to);
if let from = fromVC,
let fromView = from.view,
let to = toVC,
let toView = to.view {
let containerWidth = container.frame.size.width
// Set the needed frames to animate.
var toInitialFrame = container.frame
var fromDestinationFrame = fromView.frame
if self.navigationControllerOperation == .push {
toInitialFrame.origin.x = containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = -containerWidth;
}
else if self.navigationControllerOperation == .pop {
toInitialFrame.origin.x = -containerWidth;
toView.frame = toInitialFrame;
fromDestinationFrame.origin.x = containerWidth;
}
// Create a screenshot of the toView.
if let move = toView.snapshotView(afterScreenUpdates: true) {
move.frame = toView.frame
container.addSubview(move)
UIView.animate(withDuration: Constants.MainPage.navControllerDuration, delay: 0.0, usingSpringWithDamping: 1000, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
move.frame = container.frame;
fromView.frame = fromDestinationFrame;
}, completion: { (finished) in
if finished {
if !container.subviews.contains(toView) {
container.addSubview(toView)
}
toView.frame = container.frame
fromView.removeFromSuperview()
move.removeFromSuperview()
transitionContext.completeTransition(true)
}
})
}
}
}
Cheers.

Center window component

I have create a window component but it will randomly position whether I open its window, x and y positions will only offset the elements, not window. How do I position it to the center of the screen?
Flex 4 (AS3):
private function openDoc():void {
if (newWindow != null) newWindow.close();
newWindow = new docwin();
newWindow.width = 500;
newWindow.height = 320;
newWindow.type = "normal";
newWindow.systemChrome = "standard";
newWindow.transparent = false;
newWindow.setStyle("showFlexChrome", true);
newWindow.showStatusBar = false;
newWindow.minimizable = false;
newWindow.maximizable = false;
newWindow.resizable = false;
newWindow.open();
}
Try this:
newWindow.x = Math.ceil((Capabilities.screenResolutionX - newWindow.width) / 2);
newWindow.y = Math.ceil((Capabilities.screenResolutionY - newWindow.height) / 2);
You can use layout property of window like horizontalCentre and verticalCentre use contstraint based layout scheme
You have to position the new window with reference to stageWidth and stageHeight properties.
Assuming the origin of your new window is top left, the new location of the the windows will be:
(Stage.stageWidth - newWindow.width)/2, (Stage.stageHeight - newWindow.height)/2;

Resources