Auto Layout PG (A Definitive Guide For IOS Programmers)
Auto Layout PG (A Definitive Guide For IOS Programmers)
Auto Layout PG (A Definitive Guide For IOS Programmers)
Contents
Debugging 20
Debugging Workflow 20 Handling Ambiguity 22
Figures
Debugging 20
Figure 4-1 Figure 4-2 Constraint debugging window illustrating ambiguity 22 Constraint debugging window illustrating alternate resolution of ambiguity 23
You use the Cocoa Auto Layout system to define layout constraints for user interface elements. Constraints represent relationships between user interface elements such as these views line up head to tail, or this button should move with this split view subview. When laying out the user interface, a constraint satisfaction system arranges the elements in a way that most closely meets the constraints. Important: This document has not been revised to specifically describe the auto layout behavior on iOS. It is included in the iOS Library because the underlying layout concepts are the same between OS X and iOS.
At a Glance
Cocoa Auto Layout lets you define rules for the layout of elements in your user interface. For example, you might want to specify that a text label should be anchored the appropriate distance (as defined by Aqua guidelines) from the sides of its window and an adjacent text field, and that the label should occupy just as much area as required by the string. Such a scenario is illustrated in the window on the left in the following diagram.
Cocoa Auto Layout allows individual views to declare their own preferred size, which means you dont have to specify new layout if the labels text changesfor example, because of localization, as illustrated in the right window. You dont even have to specify new layout for languages like Hebrew and Arabic in which the left to right ordering of elements themselves should generally be reversed.
Cocoa Auto Layout thus improves the traditional division of responsibility between controllers and views. Rather than you writing a monolithic, omniscient controller that calculates where views need to go for a given geometry, views use constraints to declare where they want to be. Constraints represent relationships between user interface elements such as these views line up head to tail, or this button should move with this split view subview. Constraints themselves are objectsinstances of NSLayoutConstraintthat you install on views. For more about how you architect your application to use the Auto Layout system, see Dividing Responsibility Between Controllers and Views (page 17). Typically, you specify the constraints in Interface Builder. Suppose you drag a text field out of the library, snap it into the bottom left corner of a window, then drag-resize the right edge until it snaps into the bottom right corner. Nothing further is needed to specify that when the window is resized the text field should stretch horizontally. If you need to create constraints programmatically, you typically do so using an ASCII-art inspired format string. For example, you could create a constraint to keep two buttons the standard Aqua spacing apart using a format string like this: [button1]-[button2]. For more about constraints and how to create and use them, see Constraints Express Relationships Between Views (page 7). Cocoa Auto Layout offers several features that can help you find problems if your views are not positioned as you expected. In particular, Auto Layout provides a runtime visualization. For more about diagnosing problems with constraints, see Debugging (page 20). To determine the position of views, Auto Layout adds two passes to the process of rendering a window (in addition to the display pass Cocoa had previously used). In some situations, you may take advantage of these passes to perform custom layout of your own or to force re-layout of the user interface. For more about the the Auto Layout engine, see Core Layout Runtime (page 24). The design of the Auto Layout architecture makes it fairly straightforward to incrementally move a project that uses the springs and struts model for user interface layout to use the Auto Layout system. By default the Auto Layout system translates old-style autosizing masks into constraints to provide compatibility. For more about converting an existing project, see Adopting Auto Layout (page 27). There are several code examples that illustrate how you can use the Auto Layout systemsee Cocoa Autolayout Demos . The following WWDC videos provide another introduction to auto layout as well as example layouts:
WWDC 2012: Introduction to Auto Layout for iOS and OS X WWDC 2012: Auto Layout by Example
Constraints represent relationships between user interface elements such as these views line up head to tail, or this button should move with this split view subview.
About Constraints
A constraint is basically a statement of the form y = m*x + b where
An attribute is one of left, right, top, bottom, leading, trailing, width, height, centerX, centerY, and baseline. The attributes leading and trailing are the same as left and right in English, but the UI flips in a right-to-left environment like Hebrew or Arabic. When you create constraints using Interface Builder or the visual format strings (see Create Constraints Programmatically Using an ASCII-Art Format String (page 9)), leading and trailing are the default values.
m and b are floating point values.
Constraints are not directional, and there are no restrictions on cycles. Layout itself is neither top down nor bottom up. Instead, all constraints are considered at the same time, and elements laid out in such a way that best satisfies all of the constraints.
It is not legal for a constraint to cross through a view that is manually laid out, or through a view that has a bounds transform (such as a scroll view)you can think of such views as barriers. Theres an inside world and an outside world, but the inside cannot be connected to the outside by constraints.
Constraints Express Relationships Between Views Create Constraints Programmatically Using an ASCII-Art Format String
A single hyphen denotes the standard Aqua space, so you could represent the relationship with just
[button1]-[button2]
dictionarythe keys are the names you use in the format string, the values are the corresponding views. As a convenience, NSDictionaryOfVariableBindings creates a dictionary where the keys are the same as the corresponding values variable name. The code to create the constraints thus becomes:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(button1, button2); NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"[button1]-[button2]" options:0 metrics:nil views:viewsDictionary];
The visual format language prefers good visualization over completeness of expressibility; there are therefore constraints that cannot be expressed in visual format syntax, although most of the constraints that are useful in real user interfaces can be. One useful constraint that cannot be expressed is a fixed aspect ratio (for example,
example, you could create the [button1]-[button2] constraint using the following code.
[NSLayoutConstraint constraintWithItem:button1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:button2 attribute:NSLayoutAttributeLeft multiplier:1.0 constant:-12.0];
Its algorithmically important. It is much more efficient to change the constant than to remove a constraint and add a new one. It leads to a natural way to implement operations such as dragging, which is exactly the case in which performance is most important.
Install Constraints
To make a constraint active, you must add it to a view. The view that holds the constraint must be an ancestor of the views the constraint involves, often the closest common ancestor. (This is in the existing NSView API sense of the word ancestor , where a view is an ancestor of itself.) The constraint is interpreted in the coordinate system of that view. Suppose you install [zoomableView1]-80-[zoomableView2] on the common ancestor of zoomableView1 and zoomableView2.
10
Constraints Express Relationships Between Views Auto Layout Degrades Gracefully with Unsatisfiable Constraints
The 80 is in the coordinate system of the container, which is what it looks like if you draw the constraint. If the bounds transform of either of the zoomable views changes, the space between them remains fixed.
If the bounds transform in the container itself is changed, then the space scales too.
existing constraintsremoveConstraint: and constraintsas well as other related methods. NSView also provides a method, fittingSize, which is similar to the sizeToFit method of NSControl but for arbitrary views rather than for controls. The fittingSize method returns the ideal size for the view considering only those constraints installed on the receivers subtree together with a preference for the view to be as small as possible . The fitting size is not the best size for a view in a general sensein the constraint-based system, a views best size if you consider everything has to be the size it currently has!
The addConstraint: method (or the setConstant: method of NSLayoutConstraint) logs the mutually unsatisfiable constraints and throws an exception. The exception is appropriate because this is a programmer error.
2.
The system catches the exception immediately. Although adding a constraint that cannot be satisfied is a programmer error, its one that can readily imagine occurring on a users computer, and one from which the system can recover more gracefully than crashing the program. The exception is thrown so that you notice the problem, and caught because its easier to debug the constraint system if the system does still function.
11
Constraints Express Relationships Between Views Auto Layout Degrades Gracefully with Unsatisfiable Constraints
3.
To allow layout to proceed, the system selects a constraint from among the mutually unsatisfiable set and lowers its priority from required to an internal value that is not required, but is higher priority than anything you can externally specify. The effect is that as things change going forward, this incorrectly broken constraint is the first that the system attempts to satisfy. (Note: every constraint in the set will be required to begin with, because otherwise it wouldnt be causing a problem.)
2010-08-30 03:48:18.589 ak_runner[22206:110b] Unable to simultaneously satisfy constraints: ( "<NSLayoutConstraint: 0x40082d820 H:[NSButton:0x4004ea720'OK']-(20)-| '|':NSView:0x4004ea9a0 ) >", "<NSLayoutConstraint: 0x400821b40 H:[NSButton:0x4004ea720'OK']-(29)-| '|':NSView:0x4004ea9a0 ) >" ) (Names: (Names:
Will attempt to recover by breaking constraint <NSLayoutConstraint: 0x40082d820 H:[NSButton:0x4004ea720'OK']-(20)-| '|':NSView:0x4004ea9a0 ) > (Names:
12
Width Constraint
[button(>=50)]
Connection to Superview
|-50-[orchidBox]-50-|
Vertical Layout
V:[topField]-10-[bottomField]
Flush Views
[maroonView][oceanView]
Priority
[button(100@20)]
13
Equal Widths
[button1(==button2)]
Multiple Predicates
[flexibleButton(>=70,<=100)]
The notation prefers good visualization over completeness of expressibility. There are constraints that cannot be expressed in visual format syntax, although most of the constraints that are useful in real user interfaces can be. One useful constraint that cannot be expressed is a fixed aspect ratio (for example, imageView.width = 2 * imageView.height). To create such a constraint, you must use constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:.
<visualFormatString>
<orientation> <superview>
H|V |
14
Symbol
<view> <connection> <predicateList> <simplePredicate> <predicateListWithParens> <predicate> <relation> <objectOfPredicate> <priority> <constant> <viewName>
e|-<predicateList>-|<simplePredicate>|<predicateListWithParens> <metricName>|<positiveNumber>
(<predicate>(,<predicate>)*)
(<relation>)?(<objectOfPredicate>)(@<priority>)?
==|<=|>=
<constant>|<viewName> (see note) <metricName>|<number> <metricName>|<number> Parsed as a C identifier. This must be a key mapping to an instance of NSView in the passed views dictionary.
<metricName>
Parsed as a C identifier. This must be a key mapping to an instance of NSNumber in the passed metrics dictionary. As parsed by strtod_l, with the C locale.
<number>
Note: For the objectOfPredicate production, a viewName is only acceptable if the subject of the predicate is the width or height of a view. That is, you can use [view1(==view2)] to specify that view1 and view2 have the same width.
If you make a syntactic mistake, an exception is thrown with a diagnostic message. For example:
Expected ':' after 'V' to specify vertical arrangement V|[backgroundBox]| ^
A predicate on a view's thickness must end with ')' and the view must end with ']' |[whiteBox1][blackBox4(blackWidth][redBox]|
15
16
The intrinsic size for a push button is dictated by its title and image. A buttons intrinsicContentSize method should return a size with width and height that ensure that the title text is not clipped and that all the image is visible. A horizontal slider, has an intrinsic height, but no intrinsic widththe slider artwork has no intrinsic best width. A horizontal NSSlider object returns {NSViewNoInstrinsicMetric, <slider height>}. Any user of a slider will have to provide constraints that govern the width of the slider. A container, such as an instance of NSBox, has no intrinsic content size and returns { NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric }. Its subviews may have intrinsic content sizes, but the subviews content is not intrinsic to the box itself.
17
Dividing Responsibility Between Controllers and Views A Controller Specifies How Important the Intrinsic Size Is
Strongly wants to hug its content in the vertical direction (buttons really ought to be their natural height) Weakly hugs its content horizontally (extra side padding between the title and the edge of the bezel is acceptable) Strongly resists compressing or clipping content in both directions
In the scope bar in Finder, however, the buttons only weakly resist compressing content in the horizontal direction. (You can make the window small enough that buttons start truncating their contents.) Thus, Finder should lower the priority with which these buttons resist compressing content. You set the hugging and compression priorities for a view using setContentHuggingPriority:forOrientation: and
setContentCompressionResistancePriority:forOrientation: respectively. By default, all
Views Must Tell the Auto Layout System if Their Intrinsic Size Changes
Although the Auto Layout architecture reduces the burden on the implementer of a controller class, it may slightly increase the burden on the implementer of a view class. If you implement a custom view class, you should determine whether the view has an intrinsic size, and if so implement intrinsicContentSize to return a suitable value. In addition, if a property of a view changes and that change affects the intrinsic content size, the view must call invalidateIntrinsicContentSize so that the layout system notices the change and can re-layout. In the implementation of your view class, you must ensure that if the value of any property upon which the intrinsic size depends changes, you invoke invalidateIntrinsicContentSize. For example, a text field calls invalidateIntrinsicContentSize if the string value changes.
18
Dividing Responsibility Between Controllers and Views Views Indicate Baseline Offsets
In the following example, the Aqua guide (the dashed blue line) aligns with the visual extent of the button, not with the buttons frame (the solid blue rectangle).
To allow layout based on the presentation of the content rather than the frame, views provide an alignment rect, which is what the layout actually operates on. To determine whether your override is correct, you can set the NSViewShowAlignmentRects default to YES to draw the alignment rects.
19
Debugging
In an constraint-based layout system, the ability to debug effectively is clearly very important. Cocoa Auto Layout offers several features to help you find the source of errors.
An exception is thrown if you configure constraints that cannot be satisfied. See Auto Layout Degrades Gracefully with Unsatisfiable Constraints (page 11).
Auto Layout provides runtime support for visualizing constraints. Visualization may be useful both for finding a problematic constraint, and for detecting ambiguous constraints.
You can programmatically query the system to ask about ambiguous constraints. You can send a view a hasAmbiguousLayout message to determine whether its frame is ambiguous, and visualize the ambiguity using exerciseAmbiguityInLayout (see Handling Ambiguity (page 22)).
An instrument in Instruments helps you to understand changes over time. The Auto Layout instrument may be included with the developer tools in the future, but is currently provided as sample code in a templatesee Cocoa Autolayout Demos .
Debugging Workflow
Broadly speaking, there are two phases to debugging a layout problem:
1. 2.
Map from this view is in the wrong place to this constraint (or these constraints) is (are) incorrect. Map from this constraint is incorrect to this code is incorrect.
Identify the view with an incorrect frame. It may be obvious which view has the problem; if it is not, you may find it helpful to use the NSView method _subtreeDescription to create a textual description of the view hierarchy. (The _subtreeDescription method is not public API; it is, however, permissible to use for debugging purposes.)
2.
If possible, reproduce the issue while running under the Auto Layout template in Instruments.
20
(The Auto Layout instrument may be included with the developer tools in the future, but is currently provided as sample code in a templatesee Cocoa Autolayout Demos ).
3.
Find the bad constraint or constraints. To get the constraints affecting a particular view, use
constraintsAffectingLayoutForOrientation:. You can then inspect the constraints in the
debugger. They are printed using the visual format notation. If your views have identifiers (see identifier (NSView)), they print out using the identifier in the description, like this:
<NSLayoutConstraint: 0x400bef8e0 H:[refreshSegmentedControl]-(8)-[selectViewSegmentedControl] (Names: refreshSegmentedControl:0x40048a600, selectViewSegmentedControl:0x400487cc0 ) >
4.
If its not obvious which constraint is wrong at this point, visualize the constraints on screen by passing the constraints to the window using visualizeConstraints:. Heres an example of visualizing the constraints affecting the find fields horizontal layout:
When you click a constraint, it is printed in the console. In this way you can you can determine which is which, and typically identify which is incorrect. At this point you may be informed that the layout is ambiguous (see Handling Ambiguity (page 22)).
5.
Find the code thats wrong. Sometimes, once you have identified the incorrect constraint, you will know what to do to fix it.
21
If this is not the case then, in Instruments, search for the pointer of the constraint (or some of its description). This will show you interesting events in that constraints lifecycleits creation, modification, installation into windows, and so on. For each of these you can see the backtrace where it happened. Find the stage at which things went awry, and look at the backtrace. This is the code with the problem.
Handling Ambiguity
In the above workflow, we assumed that the issue was an incorrect constraint. It is also possible that the problem is due to a missing constraint. A layout is ambiguous if it is underspecified. For example, suppose the system consists of one constraint.
x + y == 100
Because there are an infinite number of solutions to this equation, the system is ambiguous (as opposed to unsatisfiable). You can resolve it by adding a second constraint such as:
x == y
In the normal course of solving layout, the system detects unsatisfiability but not ambiguity. (It is a computationally expensive task to detect ambiguity in this system.) However, if you visualize constraints the system does check for ambiguity. If it detects an ambiguity, the visualizer presents an Exercise Ambiguity button. Figure 4-1 (page 22) shows the find panel after one of the constraints has been removed.
Figure 4-1 Constraint debugging window illustrating ambiguity
22
Exercising ambiguity (clicking the button) makes your UI toggle between different states it could be in given the current set of constraints. Figure 4-2 (page 23) shows the layout of the find panel after clicking Exercise Ambiguity. Notice the different layout.
Figure 4-2 Constraint debugging window illustrating alternate resolution of ambiguity
23
In addition to the display pass Cocoa has always used, Cocoa Auto Layout adds two more passes that occur when rendering a window: updateConstraintsIfNeeded and layoutIfNeeded. These occur in order: update constraints, layout, display. If you invoke display manually, it invokes layout and layout invokes updateConstraints. You can think of the update constraints pass as a measurement pass that communicates changes in geometry to the layout system. For example, if you change the title of a button at some point the text has to be measured, and a constraint has to be set up that communicates that information into the layout system. The update constraints pass is bottom-up. It is in the layout pass that the frames of views are updated. The layout pass is top-down. These passes have parallel sets of methods:
Display Layout Update Constraints Override
I D
displayIfNeeded
layoutIfNeeded
(NSWindow)
(NSWindow)
updateConstraintsIfNeeded
No.
(NSWindow)
N c a b b c n t u r
Perform pass at view level Sets things up and invokes the view overridable method on all views in subtree that are dirty with respect to the pass.
displayIfNeeded
(NSView)
layoutSubtreeIfNeeded
updateConstraintsForSubtreeIfNeeded
No.
(NSView)
(NSView)
N c a b b c n t u r
24
Display
Layout
Update Constraints
Override
I D
View level overridable method Subclassers should override this method to do the requested work for the receiver. This implementation should not dirty the current or previous passes or invoke later passes. Invalidation Get / set methods for invalidation of the pass.
drawRect:
layout
updateConstraints
Yes.
(NSView)
(NSView)
(NSView)
Y n m d
needsUpdateConstraints
No.
Y n
(NSView)
setNeedsUpdateConstraints:
(NSView) If youre implementing a custom view, the most common methods to interact with are the view-level overridable methods and the invalidation methods.
You can override updateConstraints to perform deferred constraint setup. You invoke setNeedsUpdateConstraints: when you have set up you want to defer. This is particularly useful when a nib file is loaded and properties are changing a lotyou wont receive updateConstraints until things have settled down. You should only override layout only if you want to do custom layout. If you do, then you also need to invoke setNeedsLayout: when something that feeds into your custom layout changes.
To reiterate: you never need to call setNeedsLayout: unless you override layout. Based on changes to constraints, the layout engine determines when layout is required. A controller object is typically more concerned with the NSWindow and NSView methods that perform the pass. These uses are more rare, though still valid and needed on occasion.
25
Perform the update constraints pass manually to make sure all constraints are up to date. You might do this before introspecting a constraint to extract a metric.
Perform the layout pass manually if you want to extract frames from views and make decisions based on them. If you dont make sure layout is up to date first, the frames wont be meaningful. An NSAlert object uses this as part of its layout. The constraint-based system doesnt deal with flowed text and line breaking any better (or worse) than springs and struts does. NSAlert sets up all its constraints except for anything giving the height of the text fields. Then it calls layoutSubtreeIfNeeded. At this point the text fields have the correct width but not the correct height. It then uses NSTextField methods to find the appropriate height for the fields given their width, and adds the height constraints.
26
Views that are aware of Auto Layout can coexist in a window with views that are not. That is, an existing project can incrementally adopt Auto Layout features. This works through the property translatesAutoresizingMaskIntoConstraints. When this property is YES, which it is by default, the autoresizing mask of a view is translated into constraints. For example, if a view is configured as in Figure 6-1 and translatesAutoresizingMaskIntoConstraints is YES, then the constraints |-20-[button]-20-| and V:|-20-[button(20)] are added to the views superview. The net effect is that unaware views behave as they did in versions of OS X prior to 10.7.
Figure 6-1 Button layout using springs and struts
If you move the button 15 points to the left (including by calling setFrame: at runtime), the new constraints would be |-5-[button]-35-| and V:|-20-[button(20)]. For views that are aware of Auto Layout, in most circumstances you will want translatesAutoresizingMaskIntoConstraints to be NO. This is because the constraints generated by translating the autoresizing mask are already sufficient to completely specify the frame of a view given its superviews frame, which is generally too much. For example, this will prevent a button from automatically assuming its optimal width when its title is changed. The principal circumstance in which you should not call
setTranslatesAutoresizingMaskIntoConstraints: is when you are not the person who specifies a
views relation to its container. For example, an NSTableRowView instance is placed by NSTableView. It might do this by allowing the autoresizing mask to be translated into constraints, or it might not. This is a private implementation detail. Other views on which you should not call setTranslatesAutoresizingMaskIntoConstraints: include an NSTableCellView, a subview of
27
NSSplitView, an NSTabViewItems view, or the content view of an NSPopover, NSWindow, or NSBox. For
those familiar with classic Cocoa layout: If you would not call setAutoresizingMask: on a view in classic style, you should not call setTranslatesAutoresizingMaskIntoConstraints: under Auto Layout. If you have a view that does its own custom layout by calling setFrame:, your existing code should work. Just dont call setTranslatesAutoresizingMaskIntoConstraints: with the argument NO on views that you place manually. To provide additional compatibility, the entire constraint-based layout system is not active at all until you install a constraint on a view. Typically this is not something you should be concerned with, but if your previous layout was delicate in that you had problems you fixed by figuring out in what order the framework called methods, you may find that these problems are back under constraint-based layout. Hopefully in most cases the easiest solution will be to adopt constraint-based layout for the affected views. If youre a framework author providing custom views and want to avoid engaging constraint-based layout in a window, you can do any necessary work in updateConstraints. This method is not called until constraint-based layout is engaged. Furthermore, one of the purposes of updateConstraints is that it has a well-defined timing, similar to awakeFromNib, so you may find your problems are easier to resolve under Auto Layout. There is an edge case to be aware of. When you implement a view, you might implement all your constraint setup in a custom updateConstraints method. However, because the constraint based system engages lazily, if nothing installs any constraint, your updateConstraints method will never be invoked. To avoid this problem, you can override requiresConstraintBasedLayout to return YES to signal that your view needs the window to use constraint based layout.
28
Notes Added to iOS Library. Added links to WWDC videos. Corrected minor code error. New document that describes the constraint-based system for laying out user interface elements.
29
Apple Inc. 2012 Apple Inc. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, mechanical, electronic, photocopying, recording, or otherwise, without prior written permission of Apple Inc., with the following exceptions: Any person is hereby authorized to store documentation on a single computer for personal use only and to print copies of documentation for personal use provided that the documentation contains Apples copyright notice. No licenses, express or implied, are granted with respect to any of the technology described in this document. Apple retains all intellectual property rights associated with the technology described in this document. This document is intended to assist application developers to develop applications only for Apple-labeled computers. Apple Inc. 1 Infinite Loop Cupertino, CA 95014 408-996-1010 Apple, the Apple logo, Aqua, Cocoa, Finder, Instruments, and OS X are trademarks of Apple Inc., registered in the U.S. and other countries. iOS is a trademark or registered trademark of Cisco in the U.S. and other countries and is used under license.
Even though Apple has reviewed this document, APPLE MAKES NO WARRANTY OR REPRESENTATION, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THIS DOCUMENT, ITS QUALITY, ACCURACY, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. AS A RESULT, THIS DOCUMENT IS PROVIDED AS IS, AND YOU, THE READER, ARE ASSUMING THE ENTIRE RISK AS TO ITS QUALITY AND ACCURACY. IN NO EVENT WILL APPLE BE LIABLE FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES RESULTING FROM ANY DEFECT OR INACCURACY IN THIS DOCUMENT, even if advised of the possibility of such damages. THE WARRANTY AND REMEDIES SET FORTH ABOVE ARE EXCLUSIVE AND IN LIEU OF ALL OTHERS, ORAL OR WRITTEN, EXPRESS OR IMPLIED. No Apple dealer, agent, or employee is authorized to make any modification, extension, or addition to this warranty. Some states do not allow the exclusion or limitation of implied warranties or liability for incidental or consequential damages, so the above limitation or exclusion may not apply to you. This warranty gives you specific legal rights, and you may also have other rights which vary from state to state.