Рамка iOS меняет одно свойство (например, ширина)
Этот вопрос изначально задавался для языка программирования target-c. На момент написания статьи Swift еще даже не существовало.
Вопрос
Можно ли изменить только одно свойство CGRect
?
Например:
self.frame.size.width = 50;
вместо
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
50);
конечно я понимаю что self.frame.size.width
только для чтения, поэтому мне интересно, как это сделать?
CSS АНАЛОГИЯ продолжайте на свой страх и риск
для тех из вас, кто знаком с CSS
Идея очень похожа на использование:
margin-left: 2px;
вместо того, чтобы изменить все значение:
margin: 5px 5px 5px 2px;
9 ответов
Чтобы ответить на ваш оригинальный вопрос: да, можно изменить только одного члена CGRect
состав. Этот код не выдает ошибок:
myRect.size.width = 50;
Однако невозможно изменить одного члена CGRect
это само свойство другого объекта. В этом очень распространенном случае вам придется использовать временную локальную переменную:
CGRect frameRect = self.frame;
frameRect.size.width = 50;
self.frame = frameRect;
Причина этого заключается в том, что с помощью метода доступа к свойству self.frame = ...
эквивалентно [self setFrame:...]
и этот аксессор всегда ожидает целого CGRect
, Смешивание в стиле C struct
доступ с точечной нотацией свойства Objective C не работает в этом случае.
Мне понравился ответ Ахмеда Халафа, но мне пришло в голову, что вы также можете просто написать несколько функций на Си... главное преимущество в том, что будет легче отслеживать ошибки в случае, если вы используете неправильный тип.
Сказав это, я написал файл.h с этими объявлениями функций:
CGRect CGRectSetWidth(CGRect rect, CGFloat width);
CGRect CGRectSetHeight(CGRect rect, CGFloat height);
CGRect CGRectSetSize(CGRect rect, CGSize size);
CGRect CGRectSetX(CGRect rect, CGFloat x);
CGRect CGRectSetY(CGRect rect, CGFloat y);
CGRect CGRectSetOrigin(CGRect rect, CGPoint origin);
И соответствующий файл.m с этими реализациями функций:
CGRect CGRectSetWidth(CGRect rect, CGFloat width) {
return CGRectMake(rect.origin.x, rect.origin.y, width, rect.size.height);
}
CGRect CGRectSetHeight(CGRect rect, CGFloat height) {
return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, height);
}
CGRect CGRectSetSize(CGRect rect, CGSize size) {
return CGRectMake(rect.origin.x, rect.origin.y, size.width, size.height);
}
CGRect CGRectSetX(CGRect rect, CGFloat x) {
return CGRectMake(x, rect.origin.y, rect.size.width, rect.size.height);
}
CGRect CGRectSetY(CGRect rect, CGFloat y) {
return CGRectMake(rect.origin.x, y, rect.size.width, rect.size.height);
}
CGRect CGRectSetOrigin(CGRect rect, CGPoint origin) {
return CGRectMake(origin.x, origin.y, rect.size.width, rect.size.height);
}
Итак, теперь, чтобы делать то, что вы хотите, вы можете просто сделать:
self.frame = CGRectSetWidth(self.frame, 50);
Получите даже Fancier (обновление я сделал через год)
Это избыточно self.frame
в этом все же. Чтобы это исправить, вы можете добавить category
на UIView
с методами, которые выглядят так:
- (void) setFrameWidth:(CGFloat)width {
self.frame = CGRectSetWidth(self.frame, width); // You could also use a full CGRectMake() function here, if you'd rather.
}
И теперь вы можете просто ввести:
[self setFrameWidth:50];
Или даже лучше:
self.frameWidth = 50;
И просто так вы можете сделать что-то вроде этого:
self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;
Вы также должны иметь это в своей категории:
- (CGFloat) frameWidth {
return self.frame.size.width;
}
Наслаждаться.
На основе решения ArtOfWarfare (что действительно здорово) я построил UIView
категория без Си-функций.
Примеры использования:
[self setFrameWidth:50];
self.frameWidth = 50;
self.frameWidth += 50;
self.frameWidth = otherView.frameWidth; // as opposed to self.frameWidth = otherView.frame.size.width;
Заголовочный файл UIView+easy_frame.h
:
@interface UIView (easy_frame)
- (void) setFrameWidth:(CGFloat)width;
- (void) setFrameHeight:(CGFloat)height;
- (void) setFrameX:(CGFloat)x;
- (void) setFrameY:(CGFloat)y;
- (CGFloat) frameWidth;
- (CGFloat) frameHeight;
- (CGFloat) frameX;
- (CGFloat) frameY;
Файл реализации UIView+easy_frame.m
:
#import "UIView+easy_frame.h"
@implementation UIView (easy_frame)
# pragma mark - Setters
- (void) setFrameWidth:(CGFloat)width
{
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
width,
self.frame.size.height);
}
- (void) setFrameHeight:(CGFloat)height
{
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
height);
}
- (void) setFrameX:(CGFloat)x
{
self.frame = CGRectMake(x,
self.frame.origin.y,
self.frame.size.width,
self.frame.size.height);
}
- (void) setFrameY:(CGFloat)y
{
self.frame = CGRectMake(self.frame.origin.x,
y,
self.frame.size.width,
self.frame.size.height);
}
# pragma mark - Getters
- (CGFloat) frameWidth
{
return self.frame.size.width;
}
- (CGFloat) frameHeight
{
return self.frame.size.height;
}
- (CGFloat) frameX
{
return self.frame.origin.x;
}
- (CGFloat) frameY
{
return self.frame.origin.y;
}
Если вам нужно выполнить такую модификацию отдельных компонентов, возможно, стоит иметь такие макросы, где-нибудь доступный для всего вашего кода:
#define CGRectSetWidth(rect, w) CGRectMake(rect.origin.x, rect.origin.y, w, rect.size.height)
#define ViewSetWidth(view, w) view.frame = CGRectSetWidth(view.frame, w)
Таким образом, когда вам нужно изменить ширину в одиночку - вы просто напишите
ViewSetWidth(self, 50);
вместо
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, 50);
Основываясь на ответе n0_quarter, вот расширение UIView, написанное на Swift:
/**
* Convenience category for manipulating UIView frames
*/
extension UIView {
//MARK: - Getters
func frameX() -> CGFloat {
return frame.origin.x
}
func frameY() -> CGFloat {
return frame.origin.y
}
func frameWidth() -> CGFloat {
return frame.size.width
}
func frameHeight() -> CGFloat {
return frame.size.height
}
//MARK: - Setters
func setFrameX(x: CGFloat) {
frame = CGRect(x: x, y: frame.origin.y, width: frame.size.width, height: frame.size.height)
}
func setFrameY(y: CGFloat) {
frame = CGRect(x: frame.origin.x, y: y, width: frame.size.width, height: frame.size.height)
}
func setFrameWidth(width: CGFloat) {
frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: width, height: frame.size.height)
}
func setFrameHeight(height: CGFloat) {
frame = CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.size.width, height: height)
}
}
В Swift вы могли бы расширить класс UIView для приложения, чтобы вы могли, например, переместить представление заголовка таблицы вверх на 10 пунктов, например так:
tableView.tableHeaderView!.frameAdj(0, -20, 0, 0)
(конечно, если вы используете AutoLayout, это может на самом деле ничего не делать...:))
Расширяя UIView (затрагивая каждый UIView и его подкласс в вашем приложении)
extension UIView {
func frameAdj(x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) {
self.frame = CGRectMake(
self.frame.origin.x + x,
self.frame.origin.y + y,
self.frame.size.width + width,
self.frame.size.height + height)
}
}
Частый вызов метода setFrame не так хорош, как:
setFrameX(x)
setFrameY(y)
setFrameWidth(width)
...
вместо этого в swift мы можем использовать параметры по умолчанию для установки нескольких параметров только в одном вызове:
func setFrame(x x: CGFloat = CGFloat.NaN, y: CGFloat = CGFloat.NaN, width: CGFloat = CGFloat.NaN, height: CGFloat = CGFloat.NaN) {
let newX = x.isNaN ? self.frame.origin.x : x
let newY = y.isNaN ? self.frame.origin.y : y
let newWidth = width.isNaN ? self.bounds.size.width : width
let newHeight = height.isNaN ? self.bounds.size.height : height
self.frame = CGRect(x: newX, y: newY, width: newWidth, height: newHeight)
}
тогда для обновления параметров фрейма нужно всего лишь:
setFrame(x: x, width: min(maxX - x, titleLabel.bounds.size.width))
setFrame(width: titleDescriptionLabel.bounds.size.width + 5, height: titleDescriptionLabel.bounds.size.height + 6)
Надеюсь, я не опоздал на вечеринку, вот мое решение в ObjC.
Для меня я предпочитаю придерживаться одной функции, а не нескольких функций, и мне нравится подход CSS, показанный постером. Ниже приведена функция, которую я добавляю в свою категорию UIView.
- (void)resetFrame:(CGRect)frame {
CGRect selfFrame = self.frame;
selfFrame.origin = CGPointMake(frame.origin.x>0 ? frame.origin.x : selfFrame.origin.x, frame.origin.y>0 ? frame.origin.y : selfFrame.origin.y);
selfFrame.size = CGSizeMake(frame.size.width>0 ? frame.size.width : selfFrame.size.width, frame.size.height>0 ? frame.size.height : selfFrame.size.height);
[self setFrame:selfFrame]; }
Показанный в примере, он просматривает каждое значение в предоставленном CGRect, и если значение больше 0, это означает, что мы хотим заменить это значение. И, следовательно, вы можете сделать CGRect(0,0,100,0), и он будет предполагать, что мы хотим заменить только ширину.
-(void) sizeScreen : (UIView *) size : (double) width : (double) height : (double) x : (double) y {
CGRect frameRect = size.frame;
frameRect.size.width = width;
frameRect.size.height = height;
frameRect.origin.x = x;
frameRect.origin.y = y;
self.view.frame = frameRect;
}
его работа отлично подходит для меня:)