I need to add some view in iOS 7. And in the same time my project have to work on iOS 6.
How can I do it programmacly? I want to see the same position of "i"-button on 2 versions of app.
Here's a category I put on a ViewController. It might not be the smartest method but it works for my views. All my apps build the UI's programatically. If you set the frame of a view to this after it has been loaded, it will behave the same in iOS 7 as in earlier versions. The code isn't kludgy in that it doesn't rely on the version rather the characteristics of the superview. I'm open to any comments.
#implementation UIViewController (effectiveContentFrame)
// determine frame size and offset to get the max frame we can get for the current view and orientation
// needed this after the introduction of iOS 7 where frames are handled slightly differently
// note: it uses the different heights/widths at the current rotation
/*
iOS 6 portrait
application Frame: = (0.000000,20.000000) 320.000000x460.000000 )
superview Frame = (0.000000,20.000000) 320.000000x460.000000 )
iOS7 portrait
application Frame: = (0.000000,20.000000) 320.000000x460.000000 )
superview Frame = (0.000000,0.000000) 320.000000x480.000000 )
*/
- (CGRect) effectiveContentFrame
{
CGRect rect;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
CGFloat statusBarHeight = 0.;
rect = self.view.superview.frame; // the frame will be relative to the superview. Note in iOS6 the root superview frame includes the
if (UIInterfaceOrientationIsPortrait(orientation))
{
rect.size.height += rect.origin.y; // total height
statusBarHeight = statusBarFrame.size.height;
}
else
{
rect.size.height += rect.origin.x; // total height
statusBarHeight = statusBarFrame.size.width;
}
CGFloat extraHeight = 0; // this is going to be how much extra height we have to take off of the total height of the frame,
// which is going to be the status bar+navbar+toolbar heights
CGFloat topHeight = 0; // this is the height of the stuff before the frame, which we are going to use as an offset
if (![UIApplication sharedApplication].statusBarHidden)
extraHeight += statusBarHeight;
if (!self.navigationController.navigationBar.isHidden)
extraHeight += self.navigationController.navigationBar.frame.size.height;
topHeight = extraHeight;
if (!self.navigationController.toolbar.isHidden)
extraHeight += self.navigationController.toolbar.frame.size.height;
rect.origin.y = topHeight - rect.origin.y; // in iOS6 the status bar and navbar is already included in the origin but not in iOS7 .
if (rect.origin.y < 0)
rect.origin.y = 0;
rect.size.height -= extraHeight; // subtract status/nav and toolbar heights from the total height left in the superview
return rect;
}
It seems that type of Top Bar of your main view is Translucent Navigation Bar. Try to change it in IB to Opaque Navigation Bar.
I have had the same problem when I upgraded to the new xCode. I think this is because in older version opaque type was first in the list and now translucent on it's place.
Also, you can try self.edgesForExtendedLayout = UIRectEdgeNone; in your UIViewController
In Xcode 5 IB you can find iOS 6 / 7 deltas. All that you need is just adjust them.
The answer in C#
public static RectangleF GetUsableContentSize(this UIViewController viewController) {
RectangleF rect;
UIInterfaceOrientation orientation = UIApplication.SharedApplication.StatusBarOrientation;
RectangleF statusBarFrame = UIApplication.SharedApplication.StatusBarFrame;
float statusBarHeight = 0.0f;
var view = viewController.View;
if (view.Superview != null)
rect = view.Superview.Frame; // the frame will be relative to the superview. Note in iOS6 the root superview frame includes the
else
rect = view.Frame;
if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown)
{
rect.Height += rect.Y; // total height
statusBarHeight = statusBarFrame.Height;
}
else
{
rect.Height += rect.X; // total height
statusBarHeight = statusBarFrame.Width;
}
float extraHeight = 0; // this is going to be how much extra height we have to take off of the total height of the frame,
// which is going to be the status bar+navbar+toolbar heights
float topHeight = 0; // this is the height of the stuff before the frame, which we are going to use as an offset
if (!UIApplication.SharedApplication.StatusBarHidden)
extraHeight += statusBarHeight;
if (!viewController.NavigationController.NavigationBar.Hidden)
extraHeight += viewController.NavigationController.NavigationBar.Frame.Height;
topHeight = extraHeight;
if (!viewController.NavigationController.Toolbar.Hidden)
extraHeight += viewController.NavigationController.Toolbar.Frame.Height;
rect.Y = topHeight - rect.Y; // in iOS6 the status bar and navbar is already included in the origin but not in iOS7 .
if (rect.Y < 0)
rect.Y = 0;
rect.Size.Height -= extraHeight; // subtract status/nav and toolbar heights from the total height left in the superview
return rect;
}
Related
I have a sencha chart with crosszooming and innerPadding like the one in the fiddle. The problem I'm experiencing is that it is not possible to pull the rectangle of the cross zoom completly down to the x-axis and to the right side of the chart. When I remove the innerPadding of the chart everything works fine.
https://fiddle.sencha.com/#fiddle/mh8
Is there a workaround for this problem?
I've forked your fiddle with a fix, here: https://fiddle.sencha.com/#fiddle/mih
NOTE: You question is tagged with Touch2.1 but your fiddle is in 2.4.1
When you define your innerPadding, the innerRegion for the chart is calculated based on your padding.
Excerpt from the source of CartesianChart
shrinkBox.left += innerPadding.left;
shrinkBox.top += innerPadding.top;
shrinkBox.right += innerPadding.right;
shrinkBox.bottom += innerPadding.bottom;
innerWidth = width - innerPadding.left - innerPadding.right;
innerHeight = height - innerPadding.top - innerPadding.bottom;
me.setInnerRegion([shrinkBox.left, shrinkBox.top, innerWidth, innerHeight]);
Now inside the onGesture (protected function) handler of CrossZoom, it actually checks the innerRegion of your chart and if your current x or y is greater than the width or height (you get this from the innerRegion of the chart) of the chart respectively, it's reset to the width/height of the chart (width and height is ). Thus, you are limited inside the padded area.
Is there a workaround for this problem?
You can work around this by overriding this behavior, but it's the expected behavior as far as Sencha is concerned.
Override onGesture function of CrossZoom so that it takes the padding offset into consideration.
//onGesture implementation in CrossZoom
onGesture: function (e) {
var me = this;
if (me.zoomAnimationInProgress) {
return;
}
if (me.getLocks()[me.getGesture()] === me) {
var chart = me.getChart(),
surface = me.getSurface(),
region = chart.getInnerRegion(),
chartWidth = region[2],
chartHeight = region[3],
xy = chart.element.getXY(),
x = e.pageX - xy[0] - region[0],
y = e.pageY - xy[1] - region[1];
if (x < 0) {
x = 0;
} else if (x > chartWidth) {
x = chartWidth; // this is where it limits you in the x-axis
}
if (y < 0) {
y = 0;
} else if (y > chartHeight) {
y = chartHeight; // this is where it limits you in the y-axis
}
me.selectionRect.setAttributes({
width: x - me.startX,
height: y - me.startY
});
if (Math.abs(me.startX - x) < 11 || Math.abs(me.startY - y) < 11) {
me.selectionRect.setAttributes({globalAlpha: 0.5});
} else {
me.selectionRect.setAttributes({globalAlpha: 1});
}
surface.renderFrame();
return false;
}
}
Refer the source code for more details and understanding.
FYI: You may again have a question that you cannot start drawing your CrossZoom overlay outside the padding region. Check onGestureStart, if you are asking that question.
I'm going to create a book shelf in one of my projects. The basic requirements are as follows:
similar to iBooks bookshelf
supports both orientations well
supports all kinds of iOS devices well (different resolutions)
supports deleting and inserting items
supports reordering of items by long press gesture
shows a hidden logo when first row is pulled down
The UICollectionView is the first option that occurred to me. It easily supports grid cells. So I googled it and found some very useful tutorials:
Bryan Hansen's UICollectionView custom layout tutorial
Mark Pospesel's How to Add a Decoration View to a UICollectionView
LXReorderableCollectionViewFlowLayout
And here is the result: (Please ignore the color inconsistency problem because of the graphics I chose are not perfect yet.)
What I did:
Created a custom layout by creating a class inherited from LXReorderableCollectionViewFlowLayout (for reordering purposes) which inherited from UICollectionFlowLayout
Added a decoration view for showing logo
Added a decoration view for showing the bookshelfs
But I ran into a few problems:
1. I can't scroll at all if the items can be shown in one screen
Then I added the following code, to make the contentsize bigger
- (CGSize)collectionViewContentSize
{
return CGSizeMake(self.collectionView.bounds.size.width, self.collectionView.bounds.size.height+100);
}
Then I can scroll now. Here I pull down the first row:
You can see the the decoration view for logo is working.
2. But I got the second set of problems when I pull up the last row:
You can see the decoration view is not added at the green box part.
3. The background of decoration view for bookshelf is getting darker and darker. (Please refer to same problem here
4. The book shelf bar sometimes moves when I reorder the items
I list some of the important code here:
- (void)prepareLayout
{
// call super so flow layout can do all the math for cells, headers, and footers
[super prepareLayout];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
NSMutableDictionary *shelfLayoutInfo = [NSMutableDictionary dictionary];
// decoration view - emblem
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
UICollectionViewLayoutAttributes *emblemAttributes =
[UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:[EmblemView kind]
withIndexPath:indexPath];
emblemAttributes.frame = [self frameForEmblem:YES];
dictionary[[EmblemView kind]] = #{indexPath: emblemAttributes};
// Calculate where shelves go in a vertical layout
int sectionCount = [self.collectionView numberOfSections];
CGFloat y = 0;
CGFloat availableWidth = self.collectionViewContentSize.width - (self.sectionInset.left + self.sectionInset.right);
int itemsAcross = floorf((availableWidth + self.minimumInteritemSpacing) / (self.itemSize.width + self.minimumInteritemSpacing));
for (int section = 0; section < sectionCount; section++)
{
y += self.headerReferenceSize.height;
//y += self.sectionInset.top;
int itemCount = [self.collectionView numberOfItemsInSection:section];
int rows = ceilf(itemCount/(float)itemsAcross)+1; // add 2 more empty row which doesn't have any data
for (int row = 0; row < rows; row++)
{
indexPath = [NSIndexPath indexPathForItem:row inSection:section];
shelfLayoutInfo[indexPath] = [NSValue valueWithCGRect:CGRectMake(0,y, self.collectionViewContentSize.width, self.itemSize.height + DECORATION_HEIGHT)];
y += self.itemSize.height;
if (row < rows - 1)
y += self.minimumLineSpacing;
}
y += self.sectionInset.bottom;
y += self.footerReferenceSize.height;
}
dictionary[[ShelfView kind]] = shelfLayoutInfo;
self.shelfLayoutInfo = dictionary;
}
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSArray *attributesArrayInRect = [super layoutAttributesForElementsInRect:rect];
// cell layout info
for (BookShelfLayoutAttributes *attribs in attributesArrayInRect)
{
attribs.zIndex = 1;
CATransform3D t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, 40);
attribs.transform3D = CATransform3DRotate(t, 15 * M_PI / 180, 1, 0, 0);
}
// Add our decoration views (shelves)
NSMutableDictionary* shelfDictionary = self.shelfLayoutInfo[[ShelfView kind]];
NSMutableArray *newArray = [attributesArrayInRect mutableCopy];
[shelfDictionary enumerateKeysAndObjectsUsingBlock:^(id key, NSValue* obj, BOOL *stop) {
if (CGRectIntersectsRect([obj CGRectValue], rect))
{
UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:[ShelfView kind] withIndexPath:key];
attributes.frame = [obj CGRectValue];
NSLog(#"decorationView rect = %#",NSStringFromCGRect(attributes.frame));
attributes.zIndex = 0;
//attributes.alpha = 0.5; // screenshots
[newArray addObject:attributes];
}
}];
attributesArrayInRect = [NSArray arrayWithArray:newArray];
NSMutableDictionary* emblemDictionary = self.shelfLayoutInfo[[EmblemView kind]];
NSMutableArray *newArray2 = [attributesArrayInRect mutableCopy];
[emblemDictionary enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *indexPath, UICollectionViewLayoutAttributes *attributes, BOOL *innerStop) {
if (CGRectIntersectsRect(rect, attributes.frame)) {
[newArray2 addObject:attributes];
}
}];
attributesArrayInRect = [NSArray arrayWithArray:newArray2];
return attributesArrayInRect;
}
I'll appreciate if you're patient enough to read this post and provide any advice or suggestions. And I'll post the complete source code if I can fix all the issues. Thank you in advance.
I would suggest to check your last row and do the [self.collectionView ScrollEnable:NO] same for first row, set background color clear of collectionView and collectionViewCell and that bookshelf image sets on background view.
I have a WPF application using Avalon Dock 2.0 as docking manager. I'm facing a problem concerning the standard positioning of new opened tabs Avalon Dock is performing.
As long as all tabs fit into the tab bar a new tab is appended on the rightmost position of the tab bar. As soon as the new tab does not fit into the bar the new tab is added at the leftmost position making the former rightmost tab dissappear.
I know this is Visual Studio standard behaviour but in my application the order has a meaning. This means a new tab should either always be added on the leftmost OR the rightmost position. The switch is very confusing for the user.
Is there a way to make Avalon Dock add a new tab always on either the leftmost or the rightmost position?
I figured it out. I changed the code in the ArrangeOverride(Size finalSize) method from DocumentPaneTabPanel to prevent the repositioning of the current tab to the first position.
It now hides tabs on the left of the current one not fitting the panel - hiding the most left ones first. The tabs on the right of the current one are hidden as well - here the most right ones are hidden first. If there is an overflow the current tab is always the rightmost on the panel. This is a bit dirty at the moment but I think implementing this in a nicer way should not be that hard.
Here is the code:
protected override Size ArrangeOverride(Size finalSize)
{
double offset = 0.0;
var children = Children.Cast<UIElement>().Where(ch => ch.Visibility != System.Windows.Visibility.Collapsed).ToList();
if (children.Count > 0)
{
//find currently selected tab
int i = 0;
for (i = 0; i < children.Count(); i++)
{
TabItem doc = (TabItem)children[i];
var layoutContent = doc.Content as LayoutContent;
if (layoutContent.IsSelected)
break;
}
//calculate how many tabs left from the currently selected would fit in the panel
int cur_ind = i;
TabItem current_item = (TabItem)children[cur_ind];
List<TabItem> t_to_display = new List<TabItem>();
while (cur_ind >= 0 && offset + current_item.DesiredSize.Width <= finalSize.Width)
{
current_item = (TabItem)children[cur_ind];
current_item.Visibility = System.Windows.Visibility.Visible;
offset += current_item.DesiredSize.Width + current_item.Margin.Left + current_item.Margin.Right;
t_to_display.Add(current_item);
--cur_ind;
}
//arrange the fitting tabs on the left
double cur_offset = offset;
foreach (TabItem t in t_to_display)
{
cur_offset -= t.DesiredSize.Width + t.Margin.Left + t.Margin.Right;
t.Arrange(new Rect(cur_offset, 0.0, t.DesiredSize.Width, finalSize.Height));
}
//arrange the tabs on the right
cur_ind = i + 1;
while (cur_ind < children.Count && offset + current_item.DesiredSize.Width <= finalSize.Width)
{
current_item = (TabItem)children[cur_ind];
current_item.Visibility = System.Windows.Visibility.Visible;
current_item.Arrange(new Rect(offset, 0.0, current_item.DesiredSize.Width, finalSize.Height));
offset += current_item.DesiredSize.Width + current_item.Margin.Left + current_item.Margin.Right;
cur_ind++;
}
while(cur_ind < children.Count)
{
current_item = (TabItem)children[cur_ind];
current_item.Visibility = System.Windows.Visibility.Hidden;
cur_ind++;
}
}
return finalSize;
}
I have a WPF app that handles WM_GETMINMAXINFO in order to customize the Window chrome and still honor the system taskbar. That is, when you maximize the window on the monitor with the taskbar on it, it will not cover the taskbar. This works fine, except the window's frame is still visible when maximized, which is both ugly and useless because the window can't be resized when it's maximized anyway.
To combat this, I figured I need to alter my handling of WM_GETMINMAXINFO to increase the size of the window like this:
var monitorInfo = new SafeNativeMethods.MONITORINFO
{
cbSize = Marshal.SizeOf(typeof(SafeNativeMethods.MONITORINFO))
};
SafeNativeMethods.GetMonitorInfo(monitor, ref monitorInfo);
var workArea = monitorInfo.rcWork;
var monitorArea = monitorInfo.rcMonitor;
minMaxInfo.ptMaxPosition.x = Math.Abs(workArea.left - monitorArea.left);
minMaxInfo.ptMaxPosition.y = Math.Abs(workArea.top - monitorArea.top);
minMaxInfo.ptMaxSize.x = Math.Abs(workArea.right - workArea.left);
minMaxInfo.ptMaxSize.y = Math.Abs(workArea.bottom - workArea.top);
// increase size to account for frame
minMaxInfo.ptMaxPosition.x -= 2;
minMaxInfo.ptMaxPosition.y -= 2;
minMaxInfo.ptMaxSize.x += 4;
minMaxInfo.ptMaxSize.y += 4;
This actually works, but my concern is the last four lines where I assume that the frame width is 2 pixels. Is there a more generic approach to obtaining the frame width so I can accommodate it in my WM_GETMINMAXINFO handler?
Thanks
Sertac got me off on the right direction by pointing out the GetSystemMetrics Win32 API. This reminded me of WPF's SystemParameters class, in which I found the FixedFrameHorizontalBorderHeight and FixedFrameVerticalBorderWidth properties. These are exactly what I needed:
// increase size to account for frame
minMaxInfo.ptMaxPosition.x -= (int)SystemParameters.FixedFrameVerticalBorderWidth;
minMaxInfo.ptMaxPosition.y -= (int)SystemParameters.FixedFrameHorizontalBorderHeight;
minMaxInfo.ptMaxSize.x += (int)(SystemParameters.FixedFrameVerticalBorderWidth * 2);
minMaxInfo.ptMaxSize.y += (int)(SystemParameters.FixedFrameHorizontalBorderHeight * 2);
This problem must have been solved a million times, but Google has not been my friend.
I need to programmatically space a set of boxes to fill a certain length and be separated by a certain distance.
This is what I want:
alt text http://img257.imageshack.us/img257/3362/spacingiwant.png
Here is what I'm getting:
alt text http://img194.imageshack.us/img194/3506/spacingiget.png
Since I'm working in Objective-C using Core Graphics, I need a series of Rects that I can draw or drop an image into. My naive attempt draws a set of boxes with a certain spacing but leaves a space at the end.
Here is my code, which is in a drawRect: method
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat barStartX = 96;
CGFloat barStartY = 64.0;
CGFloat barWidth = 16;
CGFloat barHeight = 64;
CGFloat barGutter = 8;
int barSegments = 8;
for (int segmentNumber = 0; segmentNumber <= (barSegments - 1); ++segmentNumber) {
// get the box rect
CGRect segment = CGRectMake(barStartX + (barWidth * segmentNumber), barStartY , barWidth - barGutter, barHeight);
// plot box
CGContextFillRect(context, segment);
}
Before I create an impenetrable monstrosity of one-off code that even I won't understand 6 months from now, I'm wondering if there is a general solution to this spacing problem.
The answer doesn't have to be in Objective-C, as long as it's somewhat C-like. Readability has priority over performance considerations.
I think that despite your effort, this question is a bit unclear. Here's my attempt, though.
The equation that you describes in the title is:
N*x + (N-1)*S = L
Solving that for x gives us the width necessarry for each box:
x = (L - (N-1)*S) / N
This would result in code similar to something like this:
CGContextRef context = UIGraphicsGetCurrentContext();
int barSegments = 8;
CGFloat barStartX = 96;
CGFloat barStartY = 64.0;
CGFloat barTotalWidth = 196.0;
CGFloat barHeight = 64;
CGFloat barGutter = 8;
CGFloat barWidth = (barTotalWidth - (barSegments-1)*barGutter) / barSegments;
for (int segmentNumber = 0; segmentNumber < barSegments; ++segmentNumber) {
// get the box rect
CGRect segment = CGRectMake(barStartX + ((barWidth + barGutter) * segmentNumber), barStartY , barWidth, barHeight);
// plot box
CGContextFillRect(context, segment);
}
Given n boxes of width w and spacing s, the total length will be:
l = n × w + (n-1) × s
You know all the variables. The same formula can be used to place an arbitrary box. From your diagram, we can see that the length is the same as the right-edge coordinate of the final box. So you can just use n from 1 through whatever to find the right-edge of all your boxes. Finding left edge from that is trivial: subtract w, add 1 (as a box from 80 to 80 is 1px wide).
Note that n counts from 1, not 0. You can change the formula, of course, to count from 0.
I would do something like this:
CGFloat barStartX = 96;
CGFloat barStartY = 64.0;
CGFloat barWidth = 128;
CGFloat barHeight = 64;
...
int numSegments = 8; // number of segments
CGFloat spacing = 8; // spacing between segments
CGFloat segmentWidth = (barWidth - spacing*(numSegments - 1)) / numSegments;
CGRect segmentRect = CGRectMake(barStartX, barStartY, segmentWidth, barHeight);
for (int segmentNumber = 0; segmentNumber < numSegments; segmentNumber++)
{
segmentRect.origin.x = segmentNumber*(segmentWidth + spacing);
CGContextFillRect(context, segmentRect);
}
I like to define the rectangle outside of the loop, and then, in the loop, update only the properties of the rectangle that are actually changing. In this case, it is only the x coordinate that changes every time, so the loop becomes pretty simple.
Draw blue and pink rectangles separately and draw one last blue rectangle :)
(or don't draw one last pink one)